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 的默认空排序行为

如何通过 aws redshift 管理 django'orm?

Django基础(ORM)

Django基础(ORM)