【问题标题】:Django Window annotation using combined with distinct clauseDjango Window 注释使用结合 distinct 子句
【发布时间】:2020-07-27 18:28:04
【问题描述】:

我有一个存储在 Postgres 数据库中的 Django 模型,其中包含不规则间隔的计数值:

WidgetCount
 - Time
 - Count

我正在尝试使用带有 Lag 的窗口函数来为我提供前一行的值作为注释。我的问题是当我尝试将它与一些不同的日期截断结合起来时,窗口函数使用源行而不是明确分组的行。

例如,如果我有以下行:

time                count
2020-01-20 05:00    15
2020-01-20 06:00    20
2020-01-20 09:00    30
2020-01-21 06:00    35
2020-01-21 07:00    40
2020-01-22 04:00    50
2020-01-22 06:00    54
2020-01-22 09:00    58

我想返回一个显示每天第一次阅读的查询集,我可以使用:

from django.db.models.functions import Trunc

WidgetCount.objects.distinct("date").annotate(date=Trunc("time", "day"))

这给了我:

date        count
01/01/20    15
01/01/21    35
01/01/22    50

我想添加一个注释,给出昨天的值(这样我就可以显示每天的变化)。

date        count   yesterday_count
01/01/20    15
01/01/21    35      15
01/01/22    50      35

如果我这样做:

from django.db.models.functions import Trunc, Lag
from django.db.models import Window

WidgetCount.objects.distinct("date").annotate(date=Trunc("time", "day"), yesterday_count=Window(expression=Lag("count")))

第二行返回给我 30 表示昨天的计数 - 即,它在应用 distinct 子句之前向我显示了前一行。

如果我添加这样的分区子句:

WidgetCount.objects.distinct("date").annotate(date=Trunc("time", "day"), yesterday_count=Window(expression=Lag("count"), partition_by=F("date")))

那么昨天的计数对于所有行都是无。

如果需要,我可以在 Python 中进行此计算,但这让我有点抓狂,我想知道我正在尝试做的事情是否可行。

谢谢!

【问题讨论】:

    标签: django postgresql django-orm


    【解决方案1】:

    我没有在本地测试过,但我想你想GROUP BY而不是在这里使用DISTINCT

    WidgetCount.objects.values(
        date=Trunc('time', 'day'),
    ).order_by('date').annotate(
        date_count=Sum('count'),  # Will trigger a GROUP BY date
    ).annotate(
        yesterday_count=Window(Lag('date_count')),
    )
    

    【讨论】:

    • 建议的查询不会返回我正在寻找的数字,但我会调查 groupby。谢谢。 [{'date': datetime.datetime(2020, 1, 20, 0, 0, tzinfo=<UTC>), 'date_count': 65, 'yesterday_count': None}, {'date': datetime.datetime(2020, 1, 21, 0, 0, tzinfo=<UTC>), 'date_count': 75, 'yesterday_count': 162}, {'date': datetime.datetime(2020, 1, 22, 0, 0, tzinfo=<UTC>), 'date_count': 162, 'yesterday_count': 65}]
    【解决方案2】:

    我认为主要问题是您将注释中使用的操作混合在一起生成分组查询集,例如 sum 与一个操作,该操作简单地为给定查询集中的每个记录创建一个新字段,例如 yesterday_count=Window(expression=Lag("count"))

    所以在这里订购真的很重要。所以当你尝试时:

    WidgetCount.objects.distinct("date").annotate(date=Trunc("time", "day"), yesterday_count=Window(expression=Lag("count")))
    

    结果查询集只是 WidgetCount.objects.distinct("date") 注释,不进行分组。

    我建议将您的操作解耦,以便更容易理解正在发生的事情,并注意您正在迭代 python 对象,因此不需要进行任何新查询!

    请注意以 SUM 运算为例,因为我在 FirstValue 运算符中遇到了意外错误。因此,我与 Sum 一起发布以展示保持不变的想法。只需将acc_count=Sum("count") 更改为first_count=FirstValue("count"),第一个值的想法应该相同

    for truncDate_groups in Row.objects.annotate(trunc_date=Trunc('time','day')).values("trunc_date")\
                          .annotate(acc_count=Sum("count")).values("acc_count","trunc_date")\
                          .order_by('trunc_date')\
                          .annotate(y_count=Window(Lag("acc_count")))\
                          .values("trunc_date","acc_count","y_count"):
        print(truncDate_groups)
    

    输出:

    {'trunc_date': datetime.datetime(2020, 1, 20, 0, 0, tzinfo=<UTC>), 'acc_count': 65, 'y_count': None}
    {'trunc_date': datetime.datetime(2020, 1, 21, 0, 0, tzinfo=<UTC>), 'acc_count': 75, 'y_count': 162}
    {'trunc_date': datetime.datetime(2020, 1, 22, 0, 0, tzinfo=<UTC>), 'acc_count': 162, 'y_count': 65}
    

    事实证明 FirstValue 运算符需要使用 Windows 函数,因此您不能嵌套 FirtValue 然后计算 Lag,所以在这种情况下,我不确定您是否可以这样做。问题变成了如何在没有嵌套窗口的情况下访问 First_Value 列。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-13
      • 2015-01-12
      • 2017-07-21
      • 2020-05-23
      • 1970-01-01
      • 2017-12-05
      • 2019-08-03
      • 2016-11-18
      相关资源
      最近更新 更多