Django 窗口注解,使用组合式分句。

问题描述 投票:1回答:2

我有一个Django模型,存储在Postgres DB中,由不规则间隔的计数值组成。

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")))

第二行返回的yesterday_count的值是30,也就是说,在应用分明子句之前,它显示的是前一行。

如果我添加一个partiion子句,就像这样。

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

那么 yesterday_count 在所有的行中都是 None.

如果我需要的话,我可以用Python来做这个计算,但是这让我有点抓狂,我想知道我想做的事情是否可行。

谢谢!我有一个Django模型存储在Django中。

django postgresql django-orm
2个回答
4
投票

我认为主要的问题是,你混合了在注释中使用的操作,生成一个分组查询集,如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来发帖,来演示这个想法,这个想法还是一样的。对于FirstValue的想法应该是一样的,只是改变一下 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)

OUTPUT.FirstValue操作符需要使用Windows函数,所以不能嵌套FirtValue然后计算Lag,所以在这种情况下,我不知道你是否能做到。

{'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}

所以不能先嵌套FirtValue再计算Lag 所以在这种情况下,我不确定你是否可以这样做 问题就变成了如何在不嵌套windows的情况下访问First_Value列。


0
投票

我还没有在本地测试过,但我认为你想要 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')),
)
© www.soinside.com 2019 - 2024. All rights reserved.