【问题标题】:Conditional Cumulative Average in PostgreSQLPostgreSQL 中的条件累积平均值
【发布时间】:2014-11-16 20:18:13
【问题描述】:

我有一个简单的表,就是:

    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 的更新答案,因为它会产生正确的结果。我还想知道如何改进这样的查询,因为我们有很多聚合和窗口函数。有关此主题的任何参考资料都会有所帮助。

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    我只能想到一个递归查询:

    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 抱歉,我已经检查了我的答案 - 有一个错误。我已经更新了我的答案。
    • 其实我自己想出了一个解决方案。但是这个比我的好一千倍。 :P
    【解决方案2】:

    让我们专注于此:

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

    这应该不会太难:

    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 完成其余的计算。

    【讨论】:

    • 我已经编辑了这个问题,因为它在引入新的否定行时产生了错误的结果。你能看看这个吗? :)
    猜你喜欢
    • 2015-11-19
    • 2016-05-28
    • 2015-12-10
    • 2019-12-21
    • 2016-07-11
    • 2012-06-19
    • 2022-01-04
    相关资源
    最近更新 更多