我有一个Django 1.8应用程序,我使用的是MsSQL数据库,pyodbc作为db后端(使用“django-pyodbc-azure”模块) .
我有以下型号:
class Branch(models.Model):
name = models.CharField(max_length=30)
startTime = models.DateTimeField()
class Device(models.Model):
uid = models.CharField(max_length=100, primary_key=True)
type = models.CharField(max_length=20)
firstSeen = models.DateTimeField()
lastSeen = models.DateTimeField()
class Session(models.Model):
device = models.ForeignKey(Device)
branch = models.ForeignKey(Branch)
start = models.DateTimeField()
end = models.DateTimeField(null=True, blank=True)
我需要查询会话模型,我想要排除一些具有特定设备值的记录 . 所以我发出以下查询:
sessionCount = Session.objects.filter(branch=branch)
.exclude(device__in=badDevices)
.filter(end__gte=F('start')+timedelta(minutes=30)).count()
badDevices是一个预填充的设备ID列表,包含大约60个项目 .
badDevices = ['id-1', 'id-2', ...]
此查询大约需要1.5秒才能完成 . 如果我从查询中删除排除,则需要大约250毫秒 .
我为这个查询集打印了生成的sql,并在我的数据库客户端中尝试了它 . 在那里,两个版本在大约250毫秒内执行 .
这是生成的SQL:
SELECT [session].[id], [session].[device_id], [session].[branch_id], [session].[start], [session].[end]
FROM [session]
WHERE ([session].[branch_id] = my-branch-id AND
NOT ([session].[device_id] IN ('id-1', 'id-2', 'id-3',...)) AND
DATEPART(dw, [session].[start]) = 1
AND [session].[end] IS NOT NULL AND
[session].[end] >= ((DATEADD(second, 600, CAST([session].[start] AS datetime)))))
因此,在数据库级别使用排除似乎不会影响查询性能,但在django中,如果添加排除部分,查询运行速度会慢6倍 . 可能是什么导致了这个?
1 回答
一般问题似乎是django正在做一些额外的工作来准备
exclude
条款 . 在该步骤之后,当SQL生成并发送到数据库时,django端没有任何有趣的事情可能导致如此显着的延迟 .在您的情况下,可能导致这种情况的一件事是
badDevices
的某种预处理 . 例如,如果badDevices
是QuerySet
,则django可能正在执行badDevices
查询,只是为了准备实际查询的SQL . 在device
具有非默认主键的情况下,可能会发生类似的情况 .另一件事可能会延迟SQL编写当然
django-pyodbc-azure
. 也许它在编译查询时做了一些奇怪的事情,它成为了一个瓶颈 .这是所有疯狂的推测,所以如果你仍然有这个问题,那么也发布
Device
和Branch
模型,badDevices
的确切内容和从查询生成的SQL . 那么也许至少可以消除一些场景 .编辑:我认为它必须是
Device.uid
字段 . 可能django或pyodbc被非默认主键混淆,并在生成查询时获取所有设备 . 尝试两件事:用
device_id__in
,device__pk__in
和device__uid__in
替换device__in
并再次检查每一个 . 也许更明确的查询将更容易将django转换为SQL . 你甚至可以尝试用branch_id
替换branch
,以防万一 .如果上述方法无效,请尝试使用raw SQL where子句替换exclude表达式:
如果两者都不起作用,则很可能问题出在整个查询中,而不仅仅是
exclude
. 在这种情况下还有一些选项,但首先尝试以上内容,如有必要,我会在稍后更新我的答案 .