将 DISTINCT 与多个条件聚合一起使用(注释)
Posted
技术标签:
【中文标题】将 DISTINCT 与多个条件聚合一起使用(注释)【英文标题】:Using DISTINCT with Multiple Condtional Aggregations (Annotate) 【发布时间】:2020-04-30 10:09:35 【问题描述】:Python:2.7 版本:Django 1.11
你好,
我在 Django 查询中使用带有 COUNT DISTINCT 的条件聚合时遇到问题。 问题是当我加入一个单独的表时,我对 ID 的不同计数似乎不起作用。 我有一个查询,我在其中进行条件聚合以根据其他属性计算天数和总分钟数。
在下面的例子中,我们要同时查询两个东西:
每只狗的“记录的晴天数”计数。 我们遛每只狗的总时间(请耐心等待我的例子。我尝试简化模型)
型号:
from django.db import models
class Dog(models.Model):
name = models.CharField(max_length=255)
class DailyLog(models.Model):
dog = models.ForeignKey(Dog, on_delete=models.CASCADE)
is_sunny = models.BooleanField(default=False)
class WalkSession(models.Model):
daily_log = models.ForeignKey(DailyLog, on_delete=models.CASCADE)
minutes_walked = models.IntegerField()
通过迁移填充数据:
d1 = Dog.objects.create(name="Fido")
d2 = Dog.objects.create(name="Fido2")
d3 = Dog.objects.create(name="Fido3")
dl1 = DailyLog.objects.create(dog=d1, is_sunny=True)
dl2 = DailyLog.objects.create(dog=d2, is_sunny=False)
dl3 = DailyLog.objects.create(dog=d3, is_sunny=False)
WalkSession.objects.create(daily_log=dl1, minutes_walked=100)
WalkSession.objects.create(daily_log=dl1, minutes_walked=200)
WalkSession.objects.create(daily_log=dl2, minutes_walked=50)
WalkSession.objects.create(daily_log=dl3, minutes_walked=999)
Python 控制台:
简单检查总分钟数。
DailyLog.objects.all().values('dog__name').annotate(total_minutes_walked=Sum('walksession__minutes_walked'))
Result: <QuerySet ['dog__name': 'Fido', 'total_minutes_walked': 300,
'dog__name': 'Fido2', 'total_minutes_walked': 50,
'dog__name': 'Fido3', 'total_minutes_walked': 999]>
简单检查记录的晴天数。
DailyLog.objects.all().values('dog__name').annotate(sunny_days_logged=Count(Case(When(is_sunny=True, then='id'), distinct=True)))
Result: <QuerySet ['dog__name': 'Fido', 'sunny_days_logged': 1,
'dog__name': 'Fido2', 'sunny_days_logged': 0,
'dog__name': 'Fido3', 'sunny_days_logged': 0]>
使用条件聚合连接 DailyLog 表和 WalkSession 的查询。
我们现在看到记录的晴天是“2”。我们预计这是“1”。
DailyLog.objects.all().values('dog__name').annotate(total_minutes_walked=Sum('walksession__minutes_walked'), sunny_days_logged=Count(Case(When(is_sunny=True, then='id'), distinct=True)))
Result: <QuerySet ['dog__name': 'Fido', 'total_minutes_walked': 300, 'sunny_days_logged': 2,
'dog__name': 'Fido2', 'total_minutes_walked': 50, 'sunny_days_logged': 0,
'dog__name': 'Fido3', 'total_minutes_walked': 999, 'sunny_days_logged': 0]>
我查看了生成的查询,似乎在我们使用 CASE WHEN 时删除了 DISTINCT 选项。
SELECT dogwalker_dog.name,
SUM(dogwalker_walksession.minutes_walked) AS 'total_minutes_walked',
COUNT(CASE
WHEN dogwalker_dailylog.is_sunny = true THEN dogwalker_dailylog.id ELSE NULL END) AS 'sunny_days_logged'
FROM dogwalker_dailylog
INNER JOIN dogwalker_dog
ON dogwalker_dailylog.dog_id = dogwalker_dog.id
LEFT OUTER JOIN dogwalker_walksession
ON dogwalker_dailylog.id = dogwalker_walksession.daily_log_id
GROUP BY dogwalker_dog.name
COUNT 中缺少 DISTINCT。
COUNT(DISTINCT 案例
WHEN dogwalker_dailylog.is_sunny = true THEN dogwalker_dailylog.id ELSE NULL END) AS 'sunny_days_logged'
文档确实说多个聚合可能会显示错误的结果。
参考:https://docs.djangoproject.com/en/1.11/topics/db/aggregation/#combining-multiple-aggregations 我试图使用 DISTINCT 参数来帮助解决这个问题。
为什么在使用 CASE WHEN 时会丢弃 DISTINCT? 最好将查询分开而不是尝试在一个查询中计算多项内容?【问题讨论】:
【参考方案1】:我在括号上的错误和缺少选项 output_field 选项。
下面的语句为每只狗提供了正确的晴天数。
DailyLog.objects.all().values('dog__name').annotate(total_minutes_walked=Sum('walksession__minutes_walked'), sunny_days_logged=Count(Case(When(is_sunny=True, then='id'), output_field=IntegerField()), distinct=True))
【讨论】:
以上是关于将 DISTINCT 与多个条件聚合一起使用(注释)的主要内容,如果未能解决你的问题,请参考以下文章