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

ger*_*cor 5 django django-templates query-optimization django-models 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)''')
Run Code Online (Sandbox Code Playgroud)

这两个都返回相同数量的记录(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!(幽灵般的文学参考不被忽视)

我的模板使用重新组合,然后循环重新组合.没有关系,没有使用除了builtins之外的模板标签.Select_related未被使用,我仍然只获得2个查询.看看mysql日志,果然 - 1984查询.

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

END UPDATE 1

更新2 见下面的答案

格雷格

ger*_*cor 4

好的。这是我的最终结论。虽然 Furbeenator 关于 Django 内部优化的说法是正确的,但事实证明,存在一个更大的用户错误,导致了速度减慢和上述数千个查询。

原始查询集文档中清楚地记录了当您推迟字段(即不使用SELECT * FROM ...)并仅专门选择某些字段时(SELECT Checkin.Sampleid, ...您未选择的字段仍然可以访问,但可以通过另一个数据库调用进行访问)。所以,如果您是在原始查询中选择字段的子集,并且您忘记了在模板中使用的查询中的字段,Django 会执行数据库查找来查找您在模板中引用的字段,而不是抱怨它不存在或其他什么。 ,假设您从查询中遗漏了 5 个字段(这就是我所做的),您最终在模板中引用了这些字段,并且您有 300 条记录需要循环。这会导致 1500 次额外的数据库点击才能为每个字段获取这 5 个字段记录。

所以,要小心隐藏的引用,感谢上帝Django 调试工具栏