【问题标题】:Cumulative (running) sum with django orm and postgresqldjango orm 和 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。但是是的,它是我所追求的窗口函数。

标签: python django postgresql orm


【解决方案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://stackoverflow.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 应该是数据库中表的名称。

      【讨论】:

        猜你喜欢
        • 2021-01-18
        • 2020-04-18
        • 2021-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多