我需要计算累积总和(基于组,列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)
由于阈值的原因,不可能使用标准 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)
输出:
警告:表设计为无序集,因此为了获得稳定的结果,需要顺序列(例如 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)