在组阈值后重置累积和列

ESt*_*ark -1 sql sql-server

我需要计算累积总和(基于组,列GroupNr),该总和在超过某个数字后重置,在本例中为 330。

这可以使用函数或 CTE 来完成吗?如果是这样,怎么办?

当前表

GroupNr Name    Sum     Cumsum
1       Mary      0.00     0.00
1       Jane    179.00   179.00
1       Tom     106.00   285.00
1       Joseph  175.00   460.00
1       Arthur  253.00   713.00
2       Mary      0.00     0.00
2       Jane    365.00   365.00
2       Tom     365.00   730.00
2       Joseph  365.00  1095.00
2       Arthur  365.00  1460.00
Run Code Online (Sandbox Code Playgroud)

预期表

GroupNr Name    Sum     Cumsum  Resetcumsum
1       Mary      0.00    0.00    0.00
1       Jane    179.00  179.00  179.00
1       Tom     106.00  285.00  285.00
1       Joseph  175.00  460.00  460.00 -- Reset point
1       Arthur  253.00  713.00  253.00
2       Mary      0.00    0.00    0.00
2       Jane    365.00  365.00  365.00
2       Tom     365.00  730.00  365.00
2       Joseph  365.00  1095.00 365.00
2       Arthur  365.00  1460.00 365.00
Run Code Online (Sandbox Code Playgroud)

表格代码

CREATE TABLE Table1 (
    GroupNr int,
    Name varchar(7),
    Sum numeric(14, 2),
    Cumsum numeric(14, 2)
)

INSERT INTO Table1 (GroupNr, Name, Sum, Cumsum)
VALUES (1, 'Mary', 0, 0);
INSERT INTO Table1 (GroupNr, Name, Sum, Cumsum)
VALUES (1, 'Jane', 179, 179);
INSERT INTO Table1 (GroupNr, Name, Sum, Cumsum)
VALUES (1, 'Tom', 106, 285);
INSERT INTO Table1 (GroupNr, Name, Sum, Cumsum)
VALUES (1, 'Joseph', 175, 460);
INSERT INTO Table1 (GroupNr, Name, Sum, Cumsum)
VALUES (1, 'Arthur', 253, 713);
INSERT INTO Table1 (GroupNr, Name, Sum, Cumsum)
VALUES (2, 'Mary', 0, 0);
INSERT INTO Table1 (GroupNr, Name, Sum, Cumsum)
VALUES (2, 'Jane', 365, 365);
INSERT INTO Table1 (GroupNr, Name, Sum, Cumsum)
VALUES (2, 'Tom', 365, 730);
INSERT INTO Table1 (GroupNr, Name, Sum, Cumsum)
VALUES (2, 'Joseph', 365, 1095);
INSERT INTO Table1 (GroupNr, Name, Sum, Cumsum)
VALUES (2, 'Arthur', 365, 1460);
Run Code Online (Sandbox Code Playgroud)

Luk*_*zda 6

由于阈值的原因,不可能使用标准 SUM() OVER() 来限制累积 SUM。实现这种结果的一种方法是递归 CTE:

WITH cte_r AS (
  SELECT t.*, ROW_NUMBER() OVER(PARTITION BY GroupNr ORDER BY (SELECT 1)) AS rn 
  FROM Table1 t
), cte AS (
  SELECT GroupNr, Name, [Sum], [CumSum],
        CAST([Sum] AS INT) AS ResetCumSum,
        rn
  FROM cte_r
  WHERE rn = 1
  UNION ALL
  SELECT cte_r.GroupNr, cte_r.Name, cte_r.[Sum], cte_r.[CumSum],
  CAST(CASE WHEN cte.ResetCumSum >= 330 THEN 0 ELSE cte.ResetCumSum END + cte_r.[Sum] AS INT) 
       AS ResetCumSum,
       cte_r.rn
  FROM cte
  JOIN cte_r
    ON cte.rn = cte_r.rn-1
   AND cte.GroupNr = cte_r.GroupNr
)
SELECT GroupNr, Name, [Sum], [CumSum], ResetCumSum
FROM cte
ORDER BY GroupNr, rn;
Run Code Online (Sandbox Code Playgroud)

输出:

在此输入图像描述

db<>小提琴演示

警告:表设计为无序集,因此为了获得稳定的结果,需要顺序列(例如 unqiue id、时间戳)。这里使用了模拟插入,ROW_NUMBER() OVER(PARTITION BY GroupNr ORDER BY (SELECT 1)) AS rn但它不稳定。

有关的:

条件 SUM和使用 MATCH_RECOGNIZE 相同- 在我看来最干净的方法


额外的:

古怪的更新运行总计,直到特定条件为真

免责声明:“请勿在生产中使用它!!!”

-- source table to be extended with id and Resetcumsum  columns
CREATE CLUSTERED INDEX IX_ROW_NUM  ON Table1(GroupNr, id);

DECLARE @running_total       NUMERIC(14,2) = 0
        ,@prev_running_total NUMERIC(14,2) = 0
        ,@prev_GroupNr       INT = 0;

UPDATE Table1
SET 
   @prev_running_total = @running_total
  ,@running_total = Resetcumsum = IIF(@prev_GroupNr != GroupNr 
                                      OR @running_total >= 330, 0, @running_total) 
                                    + [Sum]
  ,@prev_GroupNr = GroupNr
FROM Table1 WITH(INDEX(IX_ROW_NUM))
OPTION (MAXDOP 1);

SELECT *
FROM Table1
ORDER BY id;
Run Code Online (Sandbox Code Playgroud)

db<>小提琴演示 - 2