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

nin*_*nja 77 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() 
    ......
Run Code Online (Sandbox Code Playgroud)

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

在sql中它基本上是

SELECT actor, COUNT(*) as total 
FROM Transaction 
GROUP BY actor 
ORDER BY total DESC
Run Code Online (Sandbox Code Playgroud)

Alv*_*aro 139

根据文档,您应该使用:

from django.db.models import Count
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
Run Code Online (Sandbox Code Playgroud)

values():指定将用于"分组依据"的列

Django文档:

"当使用values()子句约束结果集中返回的列时,用于评估注释的方法略有不同.原始结果根据原始QuerySet中的每个结果返回带注释的结果,而不是到values()子句中指定的字段的唯一组合"

annotate():指定对分组值的操作

Django文档:

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

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

order by子句是自解释的.

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

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

  • 值得注意的是:如果使用`Count`(以及其他聚合器),传递给`Count`的值不会影响聚合,只会影响最终值的名称.聚合器按"值"(如上所述)的唯一组合分组,而不是传递给"Count"的值. (2认同)
  • @kronosapiens 至少现在确实会影响它(我正在使用 Django 2.1.4)。在这个例子中,`total` 是给定的名称,在 sql 中使用的计数是 `COUNT('actor')` 这在这种情况下无关紧要,但如果例如 `values('x', 'y')。 annotate(count=Count('x'))`,你会得到`COUNT(x)`,而不是`COUNT(*)` 或`COUNT(x, y)`,只是在`./manage 中试了一下。 py外壳` (2认同)
  • 自从这个答案以来很多年了,但没有人指出: .order_by('total') 应该是 .order_by('-total') 因为OP要求 DESC order by (2认同)

Krz*_*iek 28

就像@Alvaro已经回答了Django的直接等同GROUP BY声明:

SELECT actor, COUNT(*) AS total 
FROM Transaction 
GROUP BY actor
Run Code Online (Sandbox Code Playgroud)

是通过使用values()annotate()方法如下:

Transaction.objects.values('actor').annotate(total=Count('actor')).order_by()
Run Code Online (Sandbox Code Playgroud)

但是还必须指出一件事:

如果模型具有定义的默认排序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']
Run Code Online (Sandbox Code Playgroud)

然后这种方法不起作用:

Transaction.objects.values('actor').annotate(total=Count('actor'))
Run Code Online (Sandbox Code Playgroud)

这是因为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"
Run Code Online (Sandbox Code Playgroud)

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

请参阅:官方Django文档中与默认排序或order_by()的交互.

  • `.order_by()`从Meta中的'ordering`中救了我. (2认同)