使用窗口函数按时间条件计算移动总和/计数并过滤PostgreSQL

问题描述

我想在第30天的行中计算前29天的总和,我使用过滤器和窗口函数,但是FILTER不起作用,

如果我使用它,它仍然从头到尾求和:

Select *,Sum(quantity) filter (where time between time - interval '29 day' and time) over ()
from t1 

如果我使用它,则显示空列

Select *,Sum(quantity) filter (where time between time - interval '29 day' and time - interval '1 day') over ()
from t1

数据,为简单起见,我减少了列数

Time        sum_quantity
2020-01-01  1
2020-01-02  2
2020-01-03  3
2020-01-04  6
    ....
2020-01-30  100

数据类型:时间是日期,数量是整数

所需结果: 应该与第一个表格具有相同的列,并添加此移动总和列

第30天=每30天第1天到第29天的总量

如何解决此问题

解决方法

您希望使用带有range的窗框定义的窗函数:

select t1.*,sum(quantity) over (order by time
                           range between interval '29 day' preceding and current row
                          ) 
from t1 ;

编辑:

如果您拥有所有日期的数据,则可以使用rows

select t1.*,sum(quantity) over (order by time
                           rows between 29 preceding and current row
                          ) 
from t1 ;

编辑II:

如果您需要在不支持range的较旧版本的Postgres中处理丢失的日子,那么扩展数据可能是最简单的方法:

select t1.*,sum(quantity) over (order by time
                           rows between 29 preceding and current row
                           ) 
from (select generate_series(min(t1.time),max(t1.time),interval '1 day') as dte
      from t1
     ) d left join
     t1
     on d.dte = t1.time;

您可能要过滤掉其他行:

select t1.*
from (select t1.*,sum(quantity) over (order by time
                                 rows between 29 preceding and current row
                                 ) as running_sum
      from (select generate_series(min(t1.time),interval '1 day') as dte
            from t1
           ) d left join
           t1
           on d.dte = t1.time
     ) t1
where t1.time is not null;
,

您的filter (where)子句始终为true,空over()子句在所有结果集中都是窗口显示的。

您应该在over子句中指定窗口,而不是在filter子句中指定窗口。可能您需要类似

sum(quantity) over (order by time rows between 29 preceding and current row)

或更好的range between...

,

这是您想要的吗?

select m1.Time,(select sum(sum_quantity) 
          from mytable m
          where m.time between (m1.time - interval '29 day') and (m1.time)) sum_total
from mytable m1
group by m1.Time
order by m1.Time;

或者也许更好:

select m1.Time,sum(m.sum_quantity) 
from mytable m
     join mytable m1 on m.time between (m1.time - interval '29 day') and (m1.time)
group by m1.Time
order by m1.Time;

这是一个演示:

DEMO

,

请在易用的地方使用条件,因为您使用的是Windows函数,因此就像条件表达式一样:

SUM(<expression>) FILTER(WHERE <condition>)
SUM(CASE WHEN <condition> THEN <expression> END)