Python: 2.7 版本: 姜戈 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}]>
使用条件聚合联接每日日志表和 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
- 计数中缺少 DISTINCT。
- 计数(不同大小写(当 dogwalker_dailylog.is_sunny = TRUE 时,dogwalker_dailylog.ID 否则空结束(作为"sunny_days_logged">
文档确实说多个聚合可能会显示错误的结果。
参考: https://docs.djangoproject.com/en/1.11/topics/db/aggregation/#combining-multiple-aggregations 我试图使用 DISTINCT 参数来帮助解决此问题。
- 为什么在使用 CASE WHEN 时会丢弃 DISTINCT?
- 最好将查询分开,而不是尝试在一个查询中计算多个内容?
我在括号上的错误和缺少选项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))