A lot has been written about it on the internet already, but I thought it was worth posting an article about calculating running totals in Microsoft SQL Server 2005 / 2008. There are several ways to do this. This article covers the fastest one.
SQL Server 2005 + has no good way of its own to calculate running totals. It cannot add the previous row to the current row by itself. You have to find a way to add the previous row to the current row.
The most common method, and the easiest to understand, is the self-join. With this method you “Join” the dataset to itself, so you can add up the result of the previous records every time. But this is not the most effective method!
The fastest method uses a table variable or temporary table (@ or #). Let me explain this with an example. We use AdventureWorksDW for it. Say we want to calculate the running totals of the internet sales per customer over time.
First we create a table variable (@) and fill it with all internet sales per customer per week. We sort the data when we insert it. This is absolutely necessary, because after that we update the records one by one with running totals.
USE AdventureWorksDWGODECLARE @RunningTotalAantal INTDECLARE @RunningTotalBedrag MONEYDECLARE @KlantNaam AS VARCHAR(128)
DECLARE @Sales TABLE (WeekNaam VARCHAR(128), KlantNaam VARCHAR(128), Aantal INT, Bedrag MONEY, AantalCumulatief INT, BedragCumulatief MONEY)
INSERT INTO @SalesSELECTDT.CalendarYear * 100 + DT.WeekNumberOfYear AS WeekNaam, DC.FirstName + ' ' + DC.LastName+ ' (' + CAST(DC.CustomerKey AS VARCHAR(16)) + ')' AS KlantNaam, SUM(FIS.OrderQuantity) AS Aantal, SUM(FIS.SalesAmount) AS Bedrag, 0, 0FROM FactInternetSales AS FISINNER JOIN DimProduct AS DP ON FIS.ProductKey = DP.ProductKeyINNER JOIN DimCustomer AS DC ON FIS.CustomerKey = DC.CustomerKeyINNER JOIN DimTime AS DT ON FIS.OrderDateKey = DT.TimeKeyGROUP BY DC.FirstName,DC.LastName, DC.CustomerKey, DT.CalendarYear,DT.WeekNumberOfYear-- Sorting is very important here!ORDER BY DC.FirstName,DC.LastName, DT.CalendarYear,DT.WeekNumberOfYearAs you can see in the example above, we sum the number of items sold and the order amount per customer per week. We set the values AantalCumulatief and BedragCumulatief to 0.
The temporary table now holds all internet sales per customer. Next we calculate the last two columns. This is what it is all about. It is a neat little trick and very effective!
UPDATE @SalesSET @RunningTotalAantal = AantalCumulatief =(CASE WHEN KlantNaam = @KlantNaam THEN @RunningTotalAantal + AantalELSE AantalEND),@RunningTotalBedrag = BedragCumulatief =(CASE WHEN KlantNaam = @KlantNaam THEN @RunningTotalBedrag + BedragELSE BedragEND),@KlantNaam = KlantNaamFROM @Sales AS SSELECT *FROM @SalesWhat you do here is fill the variable RunningTotalAantal with AantalCumulatief. For the first record it is empty. This “Aantal Cumulatief” is then filled with the field Aantal. When we take the first record, klantnaam <> @Klantnaam, because this variable is not filled yet. RunningTotalAantal and AantalCumulatief are now filled with the value of Aantal. The same happens with the column BedragCumulatief. Now comes the trick: @KlantNaam = Klantnaam. The variable is filled with the current customer name. We do this to find out if we are at a new customer, because then the counting has to start again. You have to look at the SQL carefully to understand it.
So you can use this method to calculate statistics columns in your data warehouse. I needed it to calculate the number of hours worked at customers per employee. On AdventureWorksDW you will not gain much performance compared to self joins or cursors, but if you have large tables with a few million rows, this is really fast. I tested this on a table with 5 million records. Calculating the running totals took 1 minute. Updating the FactTable also took 1 minute.
Questions? Leave a message :-)
Frequently asked questions
How do I calculate a running total in SQL Server 2005 or 2008?
SQL Server 2005 and 2008 cannot add the previous row to the current row by themselves. You can use a self-join, but a table variable or temporary table that you update row by row with an UPDATE is faster.
What is the fastest way to calculate running totals in T-SQL?
Fill a table variable (@) or temporary table (#) with sorted data. Then update the records one by one with an UPDATE that fills a variable. That is faster than a self-join.
Why is the self-join not the best method for running totals?
The self-join is the easiest to understand and the most common. But it is not the most effective method, because you join the dataset to itself.
Why do I have to sort the data when I fill the table variable?
Sorting is absolutely necessary. The UPDATE updates the records one by one with the running total, so the order of the rows decides the result.
How fast is the running total method with an UPDATE on a large table?
On a table with 5 million records, the calculation took 1 minute. Updating the FactTable also took 1 minute. On AdventureWorksDW the gain is small.

Comments
Related blogs
T-TSQL - Create 4 week Moving Total
Calculate Weeks Between Two Dates using DateDiff
Script to count nr of rows in all tables