在Django中使用带有GROUP BY子句的COUNT(DISTINCT字段)

时间:2013-08-26 19:33:25

标签: mysql django group-by django-queryset

问题

我想在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)

我的目标是在给定一段时间内的应用程序活动的情况下,为每个站点设置一个具有不同设备计数的Site对象,如

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

问题是:我不知道如何使用GROUP BY函数或其他函数添加正确的annotate

解决方案

使用distinct=True函数Count annotate使用qs = models.Site.objects.filter(habileouser__device__applicationusage__activity__range=[startDay, endDay])\ .annotate(deviceCount=Count('habileouser__device', distinct=True))\ .values('name', 'deviceCount')

{{1}}

1 个答案:

答案 0 :(得分:2)

查询集的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