如何在Django中进行SELECT COUNT(*)GROUP BY和ORDER BY?

时间:2013-09-30 18:54:21

标签: django django-queryset

我正在使用交易模型来跟​​踪通过系统的所有事件

class Transaction(models.Model):
    actor = models.ForeignKey(User, related_name="actor")
    acted = models.ForeignKey(User, related_name="acted", null=True, blank=True)
    action_id = models.IntegerField() 
    ......

如何在我的系统中获得前5名演员?

在sql中它基本上是

SELECT actor, COUNT(*) as total 
FROM Transaction 
GROUP BY actor 
ORDER BY total DESC

2 个答案:

答案 0 :(得分:124)

根据文档,您应该使用:

from django.db.models import Count
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')

values():指定将使用哪些列"按"

分组

Django docs:

  

"当使用values()子句约束列时   在结果集中返回,评估注释的方法是   稍微不一样。而不是为每个返回带注释的结果   结果在原始QuerySet中,原始结果被分组   根据中指定的字段的唯一组合   values()子句"

annotate():指定对分组值

的操作

Django docs:

  

生成汇总值的第二种方法是为QuerySet中的每个对象生成一个独立的摘要。例如,如果你   正在检索书籍列表,您可能想知道有多少作者   为每本书做出了贡献。每本书都有多对多的关系   与作者;我们想要总结每本书的这种关系   在QuerySet中。

     

可以使用annotate()子句生成每个对象的摘要。   当指定annotate()子句时,QuerySet中的每个对象   将使用指定的值进行注释。

order by子句是不言自明的。

总结一下:你分组,生成一个作者的查询集,添加注释(这将为返回的值添加一个额外的字段),最后,你按这个值排序

有关更多见解,请参阅https://docs.djangoproject.com/en/dev/topics/db/aggregation/

答案 1 :(得分:21)

就像@Alvaro回答了Django对GROUP BY语句的直接等价:

SELECT actor, COUNT(*) AS total 
FROM Transaction 
GROUP BY actor

通过使用values()annotate()方法,如下所示:

Transaction.objects.values('actor').annotate(total=Count('actor')).order_by()

然而,还有一件事需要指出:

如果模型具有class Meta中定义的默认排序,则.order_by()子句必须具有正确的结果。即使没有预定排序,也不能跳过它。

此外,对于高质量的代码,建议在.order_by()之后始终放置annotate()子句,即使没有class Meta: ordering。这种方法将使声明具有前瞻性:无论将来对class Meta: ordering进行任何更改,它都将按预期工作。

让我举个例子。如果模型有:

class Transaction(models.Model):
    actor = models.ForeignKey(User, related_name="actor")
    acted = models.ForeignKey(User, related_name="acted", null=True, blank=True)
    action_id = models.IntegerField()

    class Meta:
        ordering = ['id']

然后这种方法不起作用:

Transaction.objects.values('actor').annotate(total=Count('actor'))

那是因为Django在GROUP BY

中的每个字段上执行了额外的class Meta: ordering

如果要打印查询:

>>> print Transaction.objects.values('actor').annotate(total=Count('actor')).query
  SELECT "Transaction"."actor_id", COUNT("Transaction"."actor_id") AS "total"
  FROM "Transaction"
  GROUP BY "Transaction"."actor_id", "Transaction"."id"

很明显,聚合不会按预期工作,因此必须使用.order_by()子句来清除此行为并获得正确的聚合结果。

请参阅Django官方文档中的Interaction with default ordering or order_by()