【问题标题】:Django: alternative to using annotate(Count()) for speedDjango:替代使用 annotate(Count()) 来提高速度
【发布时间】:2016-05-11 10:41:58
【问题描述】:

有两个模型具有一对多关系,A->{B}。在使用过滤器()后,我正在计算有多少条 A 的记录与相同的 B 相同。然后我需要根据连接到它们的最多 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 记录,使用我的笔记本电脑,一次查询需要大约 2 秒。我将查询分解为多个部分并对其进行计时,似乎主要瓶颈是 annotate()。

有什么方法可以用 Django 更快地做到这一点?

【问题讨论】:

    标签: django django-models


    【解决方案1】:

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

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

    【讨论】:

    • 如果我在 values() 调用之前这样做(之后不可能),时间保持不变。还是您的意思是我需要在 filter() 之前执行此操作,然后一组调用会更快?
    • 你试过用print(data.sql)查看SQL吗?
    • 我不确定您是否想要在这个实例中加入 A 模型:我们没有使用 A 模型中的任何东西,除了它的主键,它已经在B 表。
    【解决方案2】:

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

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

    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
    

    【讨论】:

      猜你喜欢
      • 2019-01-01
      • 1970-01-01
      • 2019-11-15
      • 2021-11-28
      • 2020-07-12
      • 2021-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多