【问题标题】:Django: Why is Foo.objects.extra(...) So Much Faster Than Foo.objects.raw?Django:为什么 Foo.objects.extra(...) 比 Foo.objects.raw 快得多?
【发布时间】:2026-01-09 17:35:02
【问题描述】:

所以我正在尝试优化一个相当奇怪的查询,但这是一个遗留数据库,所以我只用我所拥有的。这些是我正在尝试的查询。此时它们提供相同的输出。 w 是我的查询集。

def future_schedule(request):

    past = datetime.date.today()-datetime.timedelta(days=730)

    extra_select = {
        'addlcomplete': 'SELECT Complete FROM tblAdditionalDates WHERE Checkin.ShortSampleID = tblAdditionalDates.ShortSampleID',
        'addldate': 'SELECT AddlDate FROM tblAdditionalDates WHERE Checkin.ShortSampleID = tblAdditionalDates.ShortSampleID'
    }
    extra_where = ['''(Checkin.Description <> "Sterilization Permit" AND Checkin.Description <> "Registration State" AND Checkin.Description <> "Miscellaneous" AND Checkin.Description <> "Equipment Purchase" AND Checkin.DateArrived > %s AND Checkin.DateCompleted IS NULL AND Checkin.Canceled = 0) OR (Checkin.Description <> "Sterilization Permit" AND Checkin.Description <> "Registration State" AND Checkin.Description <> "Miscellaneous" AND Checkin.Description <> "Equipment Purchase" AND Checkin.DateArrived > %s AND Checkin.DateCompleted IS NOT NULL AND Checkin.DateFinalCompleted IS NULL AND Checkin.DateFinalExpected IS NOT NULL AND Checkin.Canceled = 0) '''
    ]
    extra_params = [past, past]

    w = Checkin.objects.extra(select=extra_select, where=extra_where, params=extra_params)

# OR This one

    w = Checkin.objects.raw('''SELECT Checkin.SampleID, Checkin.ShortSampleID, Checkin.Company, A.Complete, Checkin.HasDates, A.AddlDate FROM Checkin LEFT JOIN (SELECT ShortSampleID, Complete, AddlDate FROM tblAdditionalDates) A ON A.ShortSampleID = Checkin.ShortSampleID WHERE (Checkin.Description <> "Sterilization Permit" AND Checkin.Description <> "Registration State" AND Checkin.Description <> "Miscellaneous" AND Checkin.Description <> "Equipment Purchase" AND Checkin.DateArrived > "2009-01-01" AND Checkin.DateCompleted IS NULL AND Checkin.Canceled = 0) OR (Checkin.Description <> "Sterilization Permit" AND Checkin.Description <> "Registration State" AND Checkin.Description <> "Miscellaneous" AND Checkin.Description <> "Equipment Purchase" AND Checkin.DateArrived > "2009-01-01" AND Checkin.DateCompleted IS NOT NULL AND Checkin.DateFinalCompleted IS NULL AND Checkin.DateFinalExpected IS NOT NULL AND Checkin.Canceled = 0)''')

这两个都返回相同数量的记录 (322)。 .extra 在呈现 HTML 方面比 .raw 查询快了大约 10 秒,而且对于所有密集的目的,.raw 查询甚至稍微不那么复杂。有没有人知道为什么会这样?根据我的结构,.raw 可能是我获取所需数据的唯一方法(我需要 extra_select 字典中的 addlcomplete 和 addldate 并在 Have 子句中使用它们来进一步过滤查询集)但我当然不喜欢如何需要很长时间。是在模板层上速度较慢还是在实际查询层上?我怎样才能最好地调试这个?

感谢您帮助我们在糟糕的数据结构中进行优化。

更新 1:2011-10-03

所以我安装了 django-debugtoolbar 来窥探一下,我启用了 MySQL 常规日志记录并提出了以下建议:

使用.filter().extra() 总查询数为2。使用.raw() 总查询数1984!!!(不要忽略怪异的文学参考)

我的模板正在使用重组,然后循环通过该重组。没有关系被遵循,没有使用除了内置的模板标签。 Select_related 没有被使用,我仍然只得到 2 个查询。看mysql日志,果然——1984个查询。

查看执行的查询时,基本上每个{{ Modelinstance.field }} django 都在执行SELECT pk, field FROM Model WHERE Model.pk = Modelinstance.pk 如果你问我这似乎完全错误。我在这里遗漏了什么还是 django 真的在查询中疯狂运行?

结束更新 1

更新 2 请看下面的答案

格雷格

【问题讨论】:

    标签: django django-models django-templates query-optimization django-queryset


    【解决方案1】:

    好的。这是我的最终结论。虽然 Furbeenator 对内部 Django 优化的看法是正确的,但事实证明存在一个更大的用户错误,导致速度变慢和前面提到的数千个查询。

    Raw queryset docs 中清楚地记录了当您延迟字段(即不使用 SELECT * FROM ...)并且仅选择特定字段(SELECT Checkin.Sampleid, ... 您未选择的字段仍然可以访问但使用另一个数据库调用。因此,如果您在原始查询中选择字段的子集,并且您忘记了在模板中使用的查询中的字段,Django 将执行数据库查找以查找您在模板中引用的字段,而不是抱怨它不存在或其他什么。因此,假设您从查询中遗漏了 5 个字段(这就是我所做的),您最终在模板中引用了这些字段,并且您有 300 条正在循环的记录。这会导致额外的 1500数据库命中以获取每条记录的这 5 个字段。

    所以,小心隐藏的引用,感谢上帝Django Debug Toolbar

    【讨论】:

      【解决方案2】:

      来自优化部分:Database access optimization,他们提出了优化方法,其中之一是 extra() 方法。然后他们提到了.raw()。我的假设是他们使 raw() 更加健壮和强大,因此它提供了最大的优化灵活性。 Performing raw SQL queries 允许您做的不仅仅是 extra()。我的直觉是,它只是更注重灵活性而不是性能,并且应该尽可能使用 extra() 而不是 raw()。

      【讨论】:

      • 所以这看起来有点疯狂。这是我到目前为止所看到的。使用 .filter() 或 .extra() 与 .raw() 给我以下在渲染模板后执行的查询总数,由 django-debugtoolbar 给出 - 2 vs 1984!!!更新帖子以反映这一点,但是什么??!!