【问题标题】:Database only Running total with with conditions仅数据库 使用条件运行总计
【发布时间】:2021-11-23 18:41:07
【问题描述】:

有很多关于使用 Postgres 计算运行总数的问题,但我正在努力做一些稍微不同的事情。

我有一张像这样的表

txn_id amount
String Integer

amounts 可以是正数也可以是负数。

我正在尝试返回一个看起来像这样的表

txn_id amount running_total overage_total
String Integer Integer Integer

其中运行总计是 amount 列的运行总和,只要金额大于零,overage_total 是低于零的金额的运行总和。

一个例子是

txn_id amount
a 1
b 2
c -4
d 2
e -1

我一直在使用窗口函数来计算运行总和,但这并不是我们所需要的。

正确的表格会返回

txn_id amount running_total overage_total
a 1 1 0
b 2 3 0
c -4 0 1
d 2 2 1
e -1 1 1

目前我正在代码中执行此操作,但如果可能的话,在数据库中执行此操作真的很不可思议。

【问题讨论】:

    标签: postgresql cumulative-sum


    【解决方案1】:

    这里的模式是总有一个上限。可以通过递归 cte 来实现:

    WITH RECURSIVE cte_r AS (
      SELECT t.*, ROW_NUMBER() OVER(ORDER BY t.txn_id) AS rn FROM tab t
    ), cte AS (
      SELECT rn,
             txn_id,
             amount, 
             CASE WHEN amount <= 0 THEN 0 ELSE amount END AS total,
             CASE WHEN amount <= 0 THEN 1 ELSE 0 END AS overage_total
      FROM cte_r
      WHERE rn = 1
      UNION ALL
      SELECT cte_r.rn,
             cte_r.txn_id,
             cte_r.amount,
             CASE WHEN cte.total + cte_r.amount <= 0 THEN 0 
                            ELSE cte.total + cte_r.amount 
                       END AS total,
             cte.overage_total + CASE WHEN cte.total + cte_r.amount <= 0 
                                      THEN 1 ELSE 0 END AS overage_total
      FROM cte
      JOIN cte_r
        ON cte.rn = cte_r.rn-1
    )
    SELECT txn_id, amount, total,overage_total
    FROM cte
    ORDER BY rn;
    

    输出:

    +---------+---------+--------+---------------+
    | txn_id  | amount  | total  | overage_total |
    +---------+---------+--------+---------------+
    | a       |      1  |     1  |             0 |
    | b       |      2  |     3  |             0 |
    | c       |     -4  |     0  |             1 |
    | d       |      2  |     2  |             1 |
    | e       |     -1  |     1  |             1 |
    | f       |      2  |     3  |             1 |
    | h       |     -4  |     0  |             2 |
    +---------+---------+--------+---------------+
    

    db<>fiddle demo


    相关:Conditional SUM on Oracle7. Capping a running total

    【讨论】:

      【解决方案2】:

      一种选择是使用函数来逐行遍历并进行计算:

      CREATE FUNCTION runningTotalWithCondition() RETURNS TABLE(txn_id char(1), amount int, running_total integer, overage_total integer) AS
      $$
      
      DECLARE 
      
      running_total integer := 0;
      overage_total integer := 0;
      c CURSOR FOR SELECT * FROM t ORDER BY txn_id ASC;
      
      BEGIN
          
          FOR recordvar IN c LOOP
            IF (running_total + recordvar.amount) > 0 THEN
              running_total = running_total + recordvar.amount;
              overage_total = overage_total;
            ELSE 
              overage_total = overage_total + abs(running_total + recordvar.amount);
              running_total = 0;
            END IF;
          
            RETURN QUERY SELECT recordvar.txn_id, recordvar.amount, running_total, overage_total;
          END LOOP;
      
      END;
      
      $$ LANGUAGE plpgsql;
      

      调用函数:

      SELECT * FROM runningTotalWithCondition();
      

      【讨论】:

      • 查找窗口函数。它们的存在是为了解决累积和问题和类似的运行总计计算 - 我将其作为 SQL 面试问题给出。
      • 是的我知道,但这是一个特殊的运行总计计算(有条件),我没有找到更好的方法来解决它。
      • postgresql.org/docs/9.2/functions-conditional.html 和窗口函数 postgresql.org/docs/9.1/tutorial-window.html - 更新你的答案,我会给它一个赞成票
      • 窗口函数的问题是它们不分组。我一直在使用窗口函数,但它不太正确
      • @austinbv 我不明白您所说的“窗口函数 [...] 不分组”是什么意思。窗口函数实际上是聚合函数。他们根据自己的定义对组进行操作。 PARTITION BY 子句动态创建流组。这是事实分组 == 同步和同步 == 在集合论/逻辑编程语言中计数的直接结果。
      猜你喜欢
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-31
      • 1970-01-01
      相关资源
      最近更新 更多