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

t9aqgxwy  于 2023-04-22  发布在  Go
关注(0)|答案(4)|浏览(162)

我使用一个事务模型来跟踪系统中发生的所有事件

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
ubof19bj

ubof19bj1#

根据文档,您应该用途:

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

values():指定哪些列将用于“分组依据”
Django文档:
使用values()子句约束结果集中返回的列时,计算注解的方法略有不同。原始QuerySet中的每个结果都不是返回带注解的结果,而是根据values()子句中指定的字段的唯一组合对原始结果进行分组。
public void run():指定对分组值的运算
Django文档:
生成摘要值的第二种方法是为QuerySet中的每个对象生成独立的摘要,例如,如果你正在检索一个图书列表,你可能想知道有多少作者为每本书做出了贡献,每本书与Author有多对多的关系;我们想为QuerySet中的每本书总结这种关系。
可以使用annotate()子句生成每个对象的摘要。当指定annotate()子句时,QuerySet中的每个对象都将使用指定的值进行注解。
顺序由子句是自我解释的。
总结如下:您可以通过分组生成作者的查询集,添加注解(这将为返回值添加一个额外的字段),最后,您可以通过此值对它们进行排序
有关详细信息,请参阅https://docs.djangoproject.com/en/dev/topics/db/aggregation/
值得注意的是:如果使用Count,传递给Count的值不会影响聚合,只会影响最终值的名称。聚合器按值的唯一组合(如上所述)分组,而不是按传递给Count的值分组。以下查询相同:

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

u7up0aaq2#

就像@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()子句是必需的,以获得正确的结果。**即使没有指定顺序,也不能跳过它。

此外,对于高质量的代码,建议始终在annotate()之后放置.order_by()子句,即使没有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对class Meta: ordering中的每个字段执行额外的GROUP BY
如果要打印查询:

>>> 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()子句来清除此行为并获得正确的聚合结果。
参见:与默认排序或order_by()的交互在Django官方文档中。

vybvopom

vybvopom3#

如果你想反向(从大值到小值),只需使用- minus。

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

f1tvaqid4#

实际上,我没有使用values()和annotations合并。
下面写的代码在我尝试的时候没有工作

from django.db.models import Count

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

所以,我更喜欢下面的类型代码分组。

get_posts(user_id):
user = Post.objects.all().\
    values('posted_by').\
    annotate(count=Count("post_id"))
return user

相关问题