SQL 服务器累计突破
SQL Server Cumulative Sum breaks
我有一个金额字段,我想构建以下 table(CumulativeSum 和 GroupNo 列):
| Id | Amount | CumulativeSum | GroupNo |
| 1 | 1000 | 1000 | 0 |
| 2 | 2000 | 3000 | 0 |
| 3 | 1000 | 4000 | 0 |
| 4 | 3000 | 3000 | 1 |
| 5 | 2000 | 5000 | 1 |
| 6 | 3000 | 3000 | 2 |
| 7 | 1000 | 4000 | 2 |
| 8 | 4000 | 4000 | 3 |
| 9 | 2000 | 2000 | 4 |
注意:当 CumulativeSum 大于 5000 时,我需要它从 0 开始与新的组号 (GroupNo) 相加。
例如,第 1、2 和 3 行的总和等于 4000。第 4 行的数量为 3000,4000 + 3000 = 7000,它大于 5000,因此它必须中断并重新从 0 开始。
最简单的方法是使用递归 CTE。这些是有效的循环,其中下一个值是根据先前的值计算的。这是一个 link 的基本示例和它们如何工作的文章:https://www.sqlservertutorial.net/sql-server-basics/sql-server-recursive-cte/
在这种情况下,它检查以前的值+当前值是否超过5000;如果是,则重置计数器并将 GroupNo 加 1;否则它只是将当前值添加到 CumulativeSum。
CREATE TABLE #Amts (ID int PRIMARY KEY, Amount int);
INSERT INTO #Amts (ID, Amount)
VALUES
(1, 1000), -- | 1000 | 0 |
(2, 2000), -- | 3000 | 0 |
(3, 1000), -- | 4000 | 0 |
(4, 3000), -- | 3000 | 1 |
(5, 2000), -- | 5000 | 1 |
(6, 3000), -- | 3000 | 2 |
(7, 1000), -- | 4000 | 2 |
(8, 4000), -- | 4000 | 3 |
(9, 2000); -- | 2000 | 4 |
WITH RunningTotals AS
(SELECT A.ID, A.Amount, A.Amount AS CumulativeSum, 0 AS GroupNo
FROM #Amts A
WHERE ID = 1
UNION ALL
SELECT RT.ID + 1,
A.Amount,
CASE WHEN RT.CumulativeSum + A.Amount > 5000 THEN A.Amount
ELSE RT.CumulativeSum + A.Amount END,
CASE WHEN RT.CumulativeSum + A.Amount > 5000 THEN RT.GroupNo + 1
ELSE RT.GroupNo END
FROM RunningTotals RT
INNER JOIN #Amts A ON RT.ID + 1 = A.ID
)
SELECT *
FROM RunningTotals
OPTION (MAXRECURSION 1000);
结果如下
ID Amount CumulativeSum GroupNo
1 1000 1000 0
2 2000 3000 0
3 1000 4000 0
4 3000 3000 1
5 2000 5000 1
6 3000 3000 2
7 1000 4000 2
8 4000 4000 3
9 2000 2000 4
备注
- 这目前要求 ID 是连续的并从 1 开始。如果不是,您可能需要使用
ROW_NUMBER() OVER (ORDER BY Id)
进行初始 CTE 以获得此
- 我已经将 MAXRECURSION 1000 放在那里 - 这意味着它将执行此循环 1000 次。如果您的 table 比这个大,则需要增加该数字。
更新:修复了代码中的错误(RT.GroupNo + 1
在错误的位置)。还向输出添加了 'Amount' 列。
我有一个金额字段,我想构建以下 table(CumulativeSum 和 GroupNo 列):
| Id | Amount | CumulativeSum | GroupNo |
| 1 | 1000 | 1000 | 0 |
| 2 | 2000 | 3000 | 0 |
| 3 | 1000 | 4000 | 0 |
| 4 | 3000 | 3000 | 1 |
| 5 | 2000 | 5000 | 1 |
| 6 | 3000 | 3000 | 2 |
| 7 | 1000 | 4000 | 2 |
| 8 | 4000 | 4000 | 3 |
| 9 | 2000 | 2000 | 4 |
注意:当 CumulativeSum 大于 5000 时,我需要它从 0 开始与新的组号 (GroupNo) 相加。
例如,第 1、2 和 3 行的总和等于 4000。第 4 行的数量为 3000,4000 + 3000 = 7000,它大于 5000,因此它必须中断并重新从 0 开始。
最简单的方法是使用递归 CTE。这些是有效的循环,其中下一个值是根据先前的值计算的。这是一个 link 的基本示例和它们如何工作的文章:https://www.sqlservertutorial.net/sql-server-basics/sql-server-recursive-cte/
在这种情况下,它检查以前的值+当前值是否超过5000;如果是,则重置计数器并将 GroupNo 加 1;否则它只是将当前值添加到 CumulativeSum。
CREATE TABLE #Amts (ID int PRIMARY KEY, Amount int);
INSERT INTO #Amts (ID, Amount)
VALUES
(1, 1000), -- | 1000 | 0 |
(2, 2000), -- | 3000 | 0 |
(3, 1000), -- | 4000 | 0 |
(4, 3000), -- | 3000 | 1 |
(5, 2000), -- | 5000 | 1 |
(6, 3000), -- | 3000 | 2 |
(7, 1000), -- | 4000 | 2 |
(8, 4000), -- | 4000 | 3 |
(9, 2000); -- | 2000 | 4 |
WITH RunningTotals AS
(SELECT A.ID, A.Amount, A.Amount AS CumulativeSum, 0 AS GroupNo
FROM #Amts A
WHERE ID = 1
UNION ALL
SELECT RT.ID + 1,
A.Amount,
CASE WHEN RT.CumulativeSum + A.Amount > 5000 THEN A.Amount
ELSE RT.CumulativeSum + A.Amount END,
CASE WHEN RT.CumulativeSum + A.Amount > 5000 THEN RT.GroupNo + 1
ELSE RT.GroupNo END
FROM RunningTotals RT
INNER JOIN #Amts A ON RT.ID + 1 = A.ID
)
SELECT *
FROM RunningTotals
OPTION (MAXRECURSION 1000);
结果如下
ID Amount CumulativeSum GroupNo
1 1000 1000 0
2 2000 3000 0
3 1000 4000 0
4 3000 3000 1
5 2000 5000 1
6 3000 3000 2
7 1000 4000 2
8 4000 4000 3
9 2000 2000 4
备注
- 这目前要求 ID 是连续的并从 1 开始。如果不是,您可能需要使用
ROW_NUMBER() OVER (ORDER BY Id)
进行初始 CTE 以获得此 - 我已经将 MAXRECURSION 1000 放在那里 - 这意味着它将执行此循环 1000 次。如果您的 table 比这个大,则需要增加该数字。
更新:修复了代码中的错误(RT.GroupNo + 1
在错误的位置)。还向输出添加了 'Amount' 列。