我得到了一个包含以下列的表:ID,IsRunningTotal和Amount(就像在SQL示例的CTE中一样)。 Amount表示由IsRunningTotal标志标识的值或RunningTotal。
如果您想知道UseCase只是想象ID代表一年中的月份(例如ID:1 = 2014年1月),那么某个月的金额将作为RunningTotal(例如3月的3000)或仅作为一个值(例如1月份的1000)。
所以给出了以下示例DataSet:
ID IsRunTot Amount
1 0 1000
2 0 1000
3 1 3000
4 1 4000
5 0 1000
6 0 1000
7 1 7000
8 1 8000
现在我想分解RunningTotals来获取每个ID的简单值(这里每行1000个)。 像:
ID IsRunTot Amount Result
1 0 1000 1000
2 0 1000 1000
3 1 3000 1000
4 1 4000 1000
5 0 1000 1000
6 0 1000 1000
7 1 7000 1000
8 1 8000 1000
现在我得到了这个Mssql查询"正在进行中" (为SQL Server 2008 R2编写):
WITH MySet (ID, IsRunTot, Amount)
AS
(
SELECT 1 AS ID, 0 AS IsRunTot, 1000 AS Amount
UNION
SELECT 2 AS ID, 0 AS IsRunTot, 1000 AS Amount
UNION
SELECT 3 AS ID, 1 AS IsRunTot, 3000 AS Amount
UNION
SELECT 4 AS ID, 1 AS IsRunTot, 4000 AS Amount
UNION
SELECT 5 AS ID, 0 AS IsRunTot, 1000 AS Amount
UNION
SELECT 6 AS ID, 0 AS IsRunTot, 1000 AS Amount
UNION
SELECT 7 AS ID, 1 AS IsRunTot, 7000 AS Amount
UNION
SELECT 8 AS ID, 1 AS IsRunTot, 8000 AS Amount
)
, MySet2 (ID, IsRunTot, Amount, BreakDown)
AS
(
SELECT ID, IsRunTot, Amount, Amount AS BreakDown
FROM MySet WHERE ID = 1
UNION ALL
SELECT A.ID, A.IsRunTot, A.Amount
, CASE WHEN A.IsRunTot = 1 AND B.IsRunTot = 1 THEN A.Amount - B.Amount ELSE NULL END AS BreakDown
FROM MySet A
INNER JOIN MySet B
ON A.ID - 1 = B.ID
)
SELECT *
FROM MySet2
OPTION (MAXRECURSION 32767);
如果前任是一个正在运行的Total并且产生以下结果,则该方法有效:
ID IsRunTot Amount BreakDown
1 0 1000 1000
2 0 1000 NULL
3 1 3000 NULL
4 1 4000 1000
5 0 1000 NULL
6 0 1000 NULL
7 1 7000 NULL
8 1 8000 1000
如您所见,我错过了ID 3和7的细分结果。 如何扩展我的查询以产生所需的结果?
答案 0 :(得分:2)
此解决方案会减去之前的运行总计及其间的所有值。
;WITH MySet (ID, IsRunTot, Amount)
AS
(
SELECT 1, 0, 1000
UNION SELECT 2, 0, 1000
UNION SELECT 3, 1, 3000
UNION SELECT 4, 1, 4000
UNION SELECT 5, 0, 1000
UNION SELECT 6, 0, 1000
UNION SELECT 7, 1, 7000
UNION SELECT 8, 1, 8000
)
SELECT A.ID, A.IsRunTot, A.Amount
, BreakDown = CASE WHEN A.IsRunTot = 1 THEN A.Amount -
(SELECT SUM(B.Amount) FROM MySet B WHERE B.ID BETWEEN ISNULL(
(SELECT MAX(C.ID) FROM MySet C WHERE C.ID < A.ID AND IsRunTot = 1)
,1) AND A.ID - 1) END
FROM MySet A;
答案 1 :(得分:2)
以下内容利用CTE计算真实故障和运行总数。
DECLARE @Data TABLE (ID INT, IsRunTot BIT, Amount INT)
INSERT @Data VALUES (
1,0,1000),(
2,0,1000),(
3,1,3000),(
4,1,4000),(
5,0,1000),(
6,0,1000),(
7,1,7000),(
8,1,8000)
; WITH CTE AS (
SELECT TOP 1
ID,
IsRunTot,
Amount,
Amount AS RunningTotal,
Amount AS Breakdown
FROM @Data
ORDER BY ID
UNION ALL
SELECT
D2.ID,
D2.IsRunTot,
D2.Amount,
D1.RunningTotal + D2.Amount - (CASE WHEN D2.IsRunTot = 1 THEN D1.RunningTotal ELSE 0 END),
D2.Amount - (CASE WHEN D2.IsRunTot = 1 THEN D1.RunningTotal ELSE 0 END)
FROM CTE D1
INNER JOIN @Data D2
ON D1.ID + 1 = D2.ID
)
SELECT *
FROM CTE
这会产生输出
ID IsRunTot Amount RunningTotal Breakdown
----------- -------- ----------- ------------ -----------
1 0 1000 1000 1000
2 0 1000 2000 1000
3 1 3000 3000 1000
4 1 4000 4000 1000
5 0 1000 5000 1000
6 0 1000 6000 1000
7 1 7000 7000 1000
8 1 8000 8000 1000
答案 2 :(得分:1)
除了在其他答案中提出的基于集合的方法之外,一个选项可能是使用光标(有时可以表现得相当好,信不信由你)
SET NOCOUNT ON;
DECLARE @res TABLE (ID int, IsRunTot bit, Amount int, Breakdown int);
DECLARE @id int, @IsRunTot int, @Amount int;
DECLARE _cursor CURSOR FOR
SELECT ID, IsRunTot, Amount FROM test100 ORDER BY ID;
OPEN _cursor
FETCH NEXT FROM _cursor INTO @id, @IsRunTot, @Amount
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT @res
SELECT @ID, @IsRunTot, @Amount,
CASE WHEN @IsRunTot = 1
THEN @Amount - (SELECT SUM(breakdown) FROM @res WHERE id < @id)
ELSE @Amount
END
FETCH NEXT FROM _cursor INTO @id, @IsRunTot, @Amount
END
CLOSE _cursor;
DEALLOCATE _cursor;
SELECT * FROM @res
使用您的样本数据,结果就是:
ID IsRunTot Amount BreakDown
----------- -------- ----------- --------------------
1 0 1000 1000
2 0 1000 1000
3 1 3000 1000
4 1 4000 1000
5 0 1000 1000
6 0 1000 1000
7 1 7000 1000
8 1 8000 1000
答案 3 :(得分:1)
SQL2008支持SUM
窗口聚合函数。 MSDN article
SELECT ID, IsRunTot, Amount, SUM(Amount) OVER()*IsRunTot
FROM MySet