CREATE TABLE Sales (DayCount smallint, Sales money)CREATE CLUSTERED INDEX ndx_DayCount ON Sales(DayCount)goINSERT Sales VALUES (1,120)INSERT Sales VALUES (2,60)INSERT Sales VALUES (3,125)INSERT Sales VALUES (4,40)DECLARE @DayCount smallint, @Sales moneySET @DayCount = 5SET @Sales = 10WHILE @DayCount < 5000 BEGIN INSERT Sales VALUES (@DayCount,@Sales) SET @DayCount = @DayCount + 1 SET @Sales = @Sales + 15 END
SELECT a.DayCount, a.Sales, SUM(b.Sales) AS RunningTotalFROM Sales aCROSS JOIN Sales bWHERE (b.DayCount <= a.DayCount)GROUP BY a.DayCount,a.SalesORDER BY a.DayCount,a.Sales
-- A. COALESCE method (9sec for 5K records, 2:30 for 20K)BEGIN SELECT [Day], Sales, Sales+ COALESCE((SELECT SUM(Sales) FROM ##Sales b WHERE b.[Day] < a.[Day]),0) AS [Running Total] FROM ##Sales a ORDER BY [Day]ENDGO-- B. Cross Join method (2sec for 5K records, 0:26 for 20K)SELECT a.[Day], a.Sales, SUM(b.Sales) [Running Total]FROM ##Sales a CROSS JOIN ##Sales bWHERE (b.[Day] <= a.[Day])GROUP BY a.[Day],a.SalesORDER BY a.[Day],a.Sales-- NOTE: B & C have the same query plan. -- C. Self Join method; Equal to B (2sec for 5K records, 0:26 for 20K)SELECT a.[Day], a.Sales, SUM(b.Sales) [Running Total]FROM ##Sales aJOIN ##Sales b ON (b.[Day] <= a.[Day]) GROUP BY a.[Day],a.Sales ORDER BY a.[Day],a.Sales-- D. Cross Apply/Self-Join method (4 sec for 5K records, 1:09 for 20K)SELECT [Day], [Sales], [Running Total]FROM ##Sales a CROSS APPLY ( SELECT [Running Total] = sum(Sales) FROM ##Sales WHERE [Day] <= a.[Day] ) RT ORDER BY [Day];
-- E. rCTE method - Very fast, (17 sec for 1M rows);WITH CTE ([Day], [Sales], [Running Total]) AS ( SELECT [Day], [Sales], [Sales] FROM ##Sales WHERE [Day] = 1 UNION ALL SELECT a.[Day], a.[Sales], CTE.[Running Total] + a.[Sales] FROM CTE JOIN ##Sales a ON CTE.[Day] + 1 = a.[Day])SELECT * FROM CTEOPTION (MAXRECURSION 0)GO-- F. "Quirky Update" Method (fastest, 12 sec for 1M rows)DECLARE @PrevDay INT, @RunningTotal MONEY = 0DECLARE @sales TABLE ( [Day#] int, [Sales#] MONEY, [RunningTotal] MONEY, PRIMARY KEY([Day#] ASC));INSERT INTO @sales ([Day#],[Sales#]) SELECT * FROM ##Sales; UPDATE @sales SET @RunningTotal = RunningTotal = CASE WHEN [Day] = @PrevDay THEN @RunningTotal+[Sales#] ELSE Sales# END, @PrevDay = [Day] FROM ##Sales WITH (TABLOCKX) OPTION (MAXDOP 1)SELECT * FROM @salesGO-- G. Optimized Cursor, (44sec for 1M rows, 70 seconds without FAST_FORWARD)BEGIN DECLARE @Day int, @Sales money DECLARE @RunningTotal money = 0 DECLARE @SalesTbl_2 TABLE ( [Day] int, Sales money, RunningTotal money, PRIMARY KEY([Day]) ) DECLARE rt_cursor CURSOR FAST_FORWARD FOR SELECT [Day], Sales FROM ##Sales ORDER BY [Day] OPEN rt_cursor FETCH NEXT FROM rt_cursor INTO @Day,@Sales WHILE @@FETCH_STATUS = 0 BEGIN SET @RunningTotal = @RunningTotal + @Sales INSERT @SalesTbl_2 VALUES (@Day,@Sales,@RunningTotal) FETCH NEXT FROM rt_cursor INTO @Day,@Sales END CLOSE rt_cursor DEALLOCATE rt_cursor SELECT [Day],Sales,RunningTotal FROM @SalesTbl_2ENDGO