【问题标题】:Using DISTINCT with Multiple Condtional Aggregations (Annotate)将 DISTINCT 与多个条件聚合一起使用(注释)
【发布时间】: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?
  • 最好将查询分开而不是尝试在一个查询中计算多项内容?

【问题讨论】:

    标签: python-2.7 distinct annotate django-1.11 conditional-aggregation


    【解决方案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))
    

    【讨论】:

      猜你喜欢
      • 2022-12-15
      • 1970-01-01
      • 1970-01-01
      • 2017-06-26
      • 1970-01-01
      • 2016-07-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-16
      相关资源
      最近更新 更多