PostgreSQL中的条件累积平均值

4

我有一个简单的表格,它是:

    DROP TABLE IF EXISTS running_averages;

    CREATE TABLE running_averages
    (
            avg_id          SERIAL NOT NULL PRIMARY KEY,
            num1             integer,
            num2             integer   DEFAULT 0     

    );

    INSERT INTO running_averages(num1, num2)
    SELECT 100, 100 UNION ALL
    SELECT 200, 175 UNION ALL
    SELECT -400, NULL UNION ALL
    SELECT 300, 200 UNION ALL
    SELECT -100, NULL;

在上表中,如果“num1”列是负值,则应使用前一行的累积平均值更新“num2”列。我的当前查询是:
    SELECT *,
            num1 * num2 AS current_total,
            SUM(num1 * num2) OVER(order by avg_id) AS cumulative_sum,
            SUM(num1)  OVER(order by avg_id) AS culmulative_num1,

            CASE WHEN num1 > 0 THEN
            SUM(num1 * num2) OVER(order by avg_id) 
            / 
            SUM(num1)  OVER(order by avg_id) 
            ELSE
            0
            END AS cumulative_average
    FROM running_averages;

结果:

avg_id  num1  num2    current_total cumulative_sum   cumulative_num1 cumulative_average
1       100   100     10,000        10,000           100             100
2       200   175     35,000        45,000           300             150
3       -400          NULL          45,00            -100            0
4       300   200     60,000        105,000          200             525
5       -100          NULL          105,000          100               0

如果当前行的num1列是负数,我无法想出如何将前一行的累积平均值带入。期望的输出应该是:

avg_id  num1  num2    current_total cumulative_sum   cumulative_num1 cumulative_average
1       100   100     10,000        10,000           100             100
2       200   175     35,000        45,000           300             150
3       -400  150     -60,000       -15,00           -100            150
4       300   200     60,000        45,000           200             225
5       -100  225     -22,500       22,500           100             225

在这种情况下,我如何获取最后一行列的值?
编辑:
我编辑了上面的SQL脚本。我很喜欢Gordon Linoff答案的方法。但是很遗憾,根据脚本更改,它产生了错误的结果。
avg_id  num1  num2    new_num2
1       100   100     100
2       200   175     175
3       -400  150     150 (Correct)
4       300   200     200
5       -100  225     50  (Incorrect)

编辑2

我也测试了Multisync的答案,它也产生了错误的结果:

avg_id  num1  num2              current_total cumulative_sum   cumulative_num1 cumulative_average
1       100   100               10,000        10,000           100             100
2       200   175               35,000        45,000           300             150
3       -400  150 (Correct)     -60,000       -15,00           -100            150
4       300   200               60,000        45,000           200             225
5       -100  175 (Incorrect)   -17,500       27,500           100             275

编辑 3

我接受了Multisync的更新答案,因为它产生了正确的结果。我也想知道如何改进像这样有很多聚合和窗口函数的查询。任何关于这个主题的参考资料都会很有帮助。

2个回答

2
我能想到的只有一个递归查询:
with recursive tmp (avg_id, num1, num2, sum_m, sum_num1, last_id) as (
  select avg_id, num1, num2, num1 * num2, num1, avg_id
  from running_averages where avg_id = 1  
  union all
  select r.avg_id, r.num1, 
         case when r.num1 < 0 then t.sum_m / t.sum_num1 else r.num2 end, 
         t.sum_m + case when r.num1 < 0 then t.sum_m / t.sum_num1 else r.num2 end * r.num1,
         t.sum_num1 + r.num1,
         r.avg_id 
  from running_averages r join tmp t on r.avg_id = t.last_id + 1
)
select avg_id, num1, num2, 
       num1 * num2 AS current_total,
       SUM(num1 * num2) OVER(order by avg_id) AS cumulative_sum,
       SUM(num1) OVER(order by avg_id) AS culmulative_num1,
       SUM(num1 * num2) OVER(order by avg_id) 
       / SUM(num1) OVER(order by avg_id) AS cumulative_average
from tmp;

avg_id必须包含连续的数字(您可以使用row_number()代替,我没有使用它是为了简化)。

num2在计算过程中会发生变化,因此除了递归查询之外,我想不到其他方法(前一步的输出是下一步的输入)。


在我在真实表格中测试后,这也产生了错误的结果。我已经编辑了问题,说明为什么它显示错误的结果。 - Nancy
@Nancy,它生成了你一天前提出的问题的结果。 - Multisync
非常抱歉。在试图简化数据库脚本时,我忘记了它。 - Nancy
@Nancy 抱歉,我检查了我的答案 - 发现了一个错误。我已经更新了我的答案。 - Multisync
其实我自己想出了一个解决方案。但这个比我的好一千倍。 :P - Nancy

2

让我们重点关注这个:

在上表中,如果“num1”列是负值,则应使用前一行的累积平均值更新“num2”列。

这不应该太难:

select ra.*,
       (case when num1 >= 0 then num2
             else avg(num1) over (order by avg_id rows between unbounded preceding and 1 preceding)
        end) as new_num2
from running_averages ra;

我认为你可以使用 new_num2 进行其余的计算。


我已经编辑了问题,因为当引入新的负行时,它会产生错误的结果。你能看一下吗? :) - Nancy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接