Django:为什么Foo.objects.extra(...)比Foo.objects.raw快得多?

时间:2011-10-03 15:07:09

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

所以我试图优化一个相当奇怪的查询,但这是一个遗留数据库,所以我用我所拥有的。这些是我正在尝试的查询。它们此时提供相同的输出。 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 dict中使用addlcomplete和addldate,并在Having子句中使用它们来进一步过滤查询集)但我当然不会喜欢花多长时间。是模板层上它更慢还是实际查询层?我怎样才能最好地调试这个?

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

更新1:2011-10-03

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

使用.filter().extra()总查询次数为2.使用.raw()总查询次数为 1984 !!! (幽灵文学参考不被忽略)< / p>

我的模板正在使用重新组合,然后循环遍历该重组。没有关系,没有使用除了builtins之外的模板标签。 Select_related未被使用,我仍然只获得2个查询。看看mysql日志,肯定是 - 1984查询。

在查看已执行的查询时,基本上看起来每个{{ Modelinstance.field }} django正在执行SELECT pk, field FROM Model WHERE Model.pk = Modelinstance.pk如果您问我,这似乎完全错误。我在这里遗漏了什么,或者django真的疯狂地查询?

END UPDATE 1

更新2 见下面的答案

格雷格

2 个答案:

答案 0 :(得分:4)

确定。以下是我的最终结论。虽然Furbeenator对于内部Django优化是正确的,但事实证明有一个更大的用户错误导致了速度减慢以及前面提到的数千个查询。

Raw queryset docs中明确记录了当您推迟字段(即不使用SELECT * FROM ...)并且仅选择某些字段时(SELECT Checkin.Sampleid, ...您未选择的字段仍然可以访问但是使用另一个数据库调用。所以,如果你在原始查询中选择了一个字段子集而忘记了你在模板中使用的查询字段,Django会执行数据库查找以查找你所在的字段在模板中引用而不是抱怨它不存在或者其他什么。所以,假设你从查询中省略了5个字段(这就是我所做的),你最终在模板中引用了你有300条记录你正在循环这会产生1500个额外的数据库命中,以获得每个记录的5个字段。

所以,要注意隐藏的引用并感谢上帝Django Debug Toolbar

答案 1 :(得分:1)

从优化部分:Database access optimization,他们建议了优化方法,其中之一是extra()方法。然后他们提到.raw()。我的假设是他们使raw()更加强大和强大,因此它提供了优化的最大灵活性。 Performing raw SQL queries允许你比extra()做更多的事情。我的预感是它更倾向于灵活性而不是性能,而extra()应该尽可能地用于raw()。