在 Django 中使用带有 GROUP BY 子句的 COUNT(DISTINCT 字段)
Posted
技术标签:
【中文标题】在 Django 中使用带有 GROUP BY 子句的 COUNT(DISTINCT 字段)【英文标题】:Usage of a COUNT(DISTINCT field) with a GROUP BY clause in Django 【发布时间】:2013-08-29 09:18:30 【问题描述】:问题
我想在 Django 中使用 COUNT(DISTINCT field)
和 GROUP BY
子句。据我了解,COUNT(DISTINCT...
只能通过对查询集使用extra
来实现。
我的简化模型是:
class Site(models.Model):
name = models.CharField(max_length=128, unique=True)
class Application(models.Model):
name = models.CharField(max_length=64)
version = models.CharField(max_length=13, db_index=True)
class User(models.Model):
name = models.CharField(max_length=64)
site = models.ForeignKey(Site, db_index=True)
class Device(models.Model):
imei = models.CharField(max_length=16, unique=True)
applications = models.ManyToManyField(Application, null=True, db_index=True, through='ApplicationUsage')
user = models.ForeignKey(User, null=True, db_index=True)
class ApplicationUsage(models.Model):
activity = models.DateField(db_index=True)
application = models.ForeignKey(Application)
device = models.ForeignKey(Device)
我的目标是在给定一段时间内的应用程序活动的情况下,为每个站点创建一个包含不同设备计数的站点对象列表,例如
stats_site.name deviceCount
ALBI 32
AMPLEPUIS 42
...
我试试这个代码:
qs = models.Site.objects.filter(user__device__applicationusage__activity__range=[startDay, endDay])\
.extra(select='deviceCount' : 'COUNT(DISTINCT `stats_device`.`id`)')\
.values('name', 'deviceCount')\
生成的SQL是:
SELECT (COUNT(DISTINCT stats_device.id)) AS deviceCount, stats_site.name
FROM stats_site
INNER JOIN stats_user ON (stats_site.id = stats_user.site_id)
INNER JOIN stats_device ON (stats_user.id = stats_device.user_id)
INNER JOIN stats_applicationusage ON (stats_device.id = stats_applicationusage.device_id)
WHERE stats_applicationusage.activity BETWEEN '2013-07-01' AND '2013-07-03'
而且结果显然是错误的,因为它缺少GROUP BY
子句,应该是GROUP BY stats_site.name
问题是:我不知道如何使用annotate
函数或其他方法添加正确的GROUP BY
。
解决方案
在Count
函数上使用distinct=True
和annotate
:
qs = models.Site.objects.filter(habileouser__device__applicationusage__activity__range=[startDay, endDay])\
.annotate(deviceCount=Count('habileouser__device', distinct=True))\
.values('name', 'deviceCount')
【问题讨论】:
就个人而言,我会在一个查询中将它们全部获取,然后使用 python 进行计数,但是,您是否查看过聚合? docs.djangoproject.com/en/dev/topics/db/aggregation 是的,但是聚合只会为整个查询集提供一个值,所以它不起作用。对于使用 Python 进行计数,最终它可能是解决方案,但前提是我找不到仅使用 SQL 计数的更好方法 【参考方案1】:查询集的annotate
方法将为查询集的每个元素计算一个聚合值,当在values
调用之后使用时,将对这些值的值进行聚合。我认为这应该可行:
qs = models.Site.objects.filter(user__device__applicationusage__activity__range=[startDay, endDay]).values('name').annotate(Count('user__device', distinct=True))
如果您指定了订单,则可能需要按此处所述将其删除: https://docs.djangoproject.com/en/dev/topics/db/aggregation/#interaction-with-default-ordering-or-order-by
【讨论】:
感谢您的回答。但是,您提出的解决方案会计算所有设备,甚至是重复的设备。关键是计算不同设备的数量。添加distinct()
将不起作用,因为 distinct 也将应用于 select 子句中的所有字段,甚至是计数
根据这个答案,Count
聚合可以采用distinct=True
参数 - 我以前不知道这一点。 ***.com/questions/4048014/…以上是关于在 Django 中使用带有 GROUP BY 子句的 COUNT(DISTINCT 字段)的主要内容,如果未能解决你的问题,请参考以下文章
mysql使用带有子查询的临时表,但不是group by和order by
带有注释的Django查询集,为啥将GROUP BY应用于所有字段?
如何在 Django 中通过 group by 获得额外的列?
LINQ Group By并将Group的子列表合并回唯一列表