django orm 和 postgresql 的累积(运行)总和
Posted
技术标签:
【中文标题】django orm 和 postgresql 的累积(运行)总和【英文标题】:Cumulative (running) sum with django orm and postgresql 【发布时间】:2017-09-16 23:48:23 【问题描述】:是否可以使用 django 的 orm 计算累积(运行)总和?考虑以下模型:
class AModel(models.Model):
a_number = models.IntegerField()
带有a_number = 1
的一组数据。这样我在数据库中有多个(>1)AModel
实例,所有实例都带有a_number=1
。我希望能够返回以下内容:
AModel.objects.annotate(cumsum=??).values('id', 'cumsum').order_by('id')
>>> (id: 1, cumsum: 1, id: 2, cumsum: 2, ... id: N, cumsum: N)
理想情况下,我希望能够限制/过滤累积总和。所以在上述情况下,我想将结果限制为cumsum <= 2
我相信在 postgresql 中可以使用窗口函数实现累积和。这是如何翻译成 ORM 的?
【问题讨论】:
我不明白。什么是cumsum?而且只有一条 id=1 的记录 cumsum == 累积总和,显然这是针对多条记录的 - 进行了编辑以使其更清晰,因此数据集的大小大于一个。 我不认为你可以用 ORM 来做...改用 python 您要查找的短语是running total(或运行总和)。这是moving aggregates的一个特例。这是window functions的一种。 我认为累计和累计是一回事mathworld.wolfram.com/CumulativeSum.html。但是是的,它是我所追求的窗口函数。 【参考方案1】:作为参考,从 Django 2.0 开始,可以使用Window
函数来实现此结果:
AModel.objects.annotate(cumsum=Window(Sum('a_number'), order_by=F('id').asc()))\
.values('id', 'cumsum').order_by('id', 'cumsum')
【讨论】:
有趣的是,窗口函数不适用于 SqLite3。【参考方案2】:来自 Dima Kudosh 的回答并基于 https://***.com/a/5700744/2240489 我必须执行以下操作:
我删除了 sql 中对PARTITION BY
的引用并替换为ORDER BY
导致。
AModel.objects.annotate(
cumsum=Func(
Sum('a_number'),
template='%(expressions)s OVER (ORDER BY %(order_by)s)',
order_by="id"
)
).values('id', 'cumsum').order_by('id', 'cumsum')
这给出了以下 sql:
SELECT "amodel"."id",
SUM("amodel"."a_number")
OVER (ORDER BY id) AS "cumsum"
FROM "amodel"
GROUP BY "amodel"."id"
ORDER BY "amodel"."id" ASC, "cumsum" ASC
Dima Kudosh 的回答不是对结果求和,而是对结果求和。
【讨论】:
【参考方案3】:对于后代,我发现这对我来说是一个很好的解决方案。我不需要结果是 QuerySet,所以我可以负担得起,因为我只是要使用 D3.js 绘制数据:
import numpy as np
import datettime
today = datetime.datetime.date()
raw_data = MyModel.objects.filter('date'=today).values_list('a_number', flat=True)
cumsum = np.cumsum(raw_data)
【讨论】:
【参考方案4】:您可以尝试使用Func expression 执行此操作。
from django.db.models import Func, Sum
AModel.objects.annotate(cumsum=Func(Sum('a_number'), template='%(expressions)s OVER (PARTITION BY %(partition_by)s)', partition_by='id')).values('id', 'cumsum').order_by('id')
【讨论】:
谢谢,非常感谢您的回答。它对我来说不太奏效,我已经发布了我的修正。【参考方案5】:检查一下
AModel.objects.order_by("id").extra(select="cumsum":'SELECT SUM(m.a_number) FROM table_name m WHERE m.id <= table_name.id').values('id', 'cumsum')
其中table_name
应该是数据库中表的名称。
【讨论】:
以上是关于django orm 和 postgresql 的累积(运行)总和的主要内容,如果未能解决你的问题,请参考以下文章
在 django ORM 中使用 postgresql 窗口函数的干净方法?
如何在 Django ORM 中映射 PostgreSQL 数组字段
如何在 Django ORM 中更改 PostgreSQL 的默认空排序行为