Django:使用annotate(Count())替代速度的替代方案

时间:2016-02-02 14:51:49

标签: django django-models

有两种具有一对多关系的模型,A-> {B}。我在计算使用过滤器()后使用相同B的A的记录数。然后我需要根据连接到它们的大多数B记录提取A的前X个记录。

当前代码:

class A(models.Model):
    code = models.IntegerField()
    ...

class B(models.Model):
    a = models.ForeignKey(A)
    ...

data = B.objects.all().filter(...)

top = data.values('a',...).annotate(n=Count('a')).distinct().order_by('-n')[:X];

我有大约300k的B记录,而我的笔记本电脑一个查询需要~2s。我将查询分解为部分并计时,似乎主要的瓶颈是annotate()。

有没有办法用Django更快地做到这一点?

2 个答案:

答案 0 :(得分:0)

您应该在.select_related('a')之前在查询集中添加annotate。这将迫使django在计算它们之前加入模型。

https://docs.djangoproject.com/en/1.9/ref/models/querysets/#select-related

答案 1 :(得分:0)

我怀疑减速实际上是在DISTINCT中,而不是计数中。

使用queryset.values(x).annotate(...)时django构造查询的方式告诉它按第一个值分组,然后执行聚合。

B.objects.filter(...).values('a').annotate(n=Count('*')).order_by('-n')[:10]

这应该生成看起来像这样的SQL:

SELECT b.a, 
       count(*) AS n
  FROM b
 GROUP BY (b.a)
 ORDER BY count(*) DESC
 LIMIT 10