Click here to Skip to main content
15,881,139 members
Articles / Programming Languages / SQL

SQL. Running Totals.

Rate me:
Please Sign up or sign in to vote.
4.67/5 (2 votes)
20 Jun 2009CPOL1 min read 26.8K   10   2
Running totals in SQL

Running totals... Accountants like them so much. But there is no way in Microsoft SQL Server to make an efficient query that calculates the running total. Why? What is running total? It is just the previous running total plus the current value. But T-SQL does not give any access to the previous rows. I searched the Internet for the running totals. For every link I clicked, I saw 2 methods to calculate running totals:

  • join or subquery the table. Here, we have n2 operations
  • use cursor and temporary table. Here, we have to insert into temporary table and then to join on the basis query.

So, both ways are not so fast as I required. But one might think that by using procedural languages, it may be done very fast.

Yes! .NET does help us since Microsoft added seamless support of .NET into the SQL Server!

The idea is to create a .NET function that can remember values of previous row and force desired order for the running total in SQL query.

First, we need a function to keep the previous running total. As far as SQL Server is a multithreaded environment, we should guaranty thread-safety and keep each running total in thread-related variable. It is a good idea to use CallContext class for this purpose.

C#
using System;
using System.Runtime.Remoting.Messaging;

namespace RunningTotal
{
    public static class RunningTotalUtils
    {
        public static decimal RunningTotal(decimal currentValue)
        {
            object _lastTotal = CallContext.GetData("runningTotal");
            decimal lastTotal = _lastTotal == null ?
              0 : Convert.ToDecimal(_lastTotal);
            lastTotal += currentValue;
            CallContext.SetData("runningTotal", lastTotal);
            return lastTotal;
        }
    }
}

Now it’s time to create a T-SQL wrapper for the function:

SQL
CREATE ASSEMBLY RunningTotal
FROM 'C:\RunningTotal.dll'
WITH PERMISSION_SET = UNSAFE;
GO

CREATE FUNCTION RunningTotal (@amount decimal(18,2))
RETURNS decimal(18,2)
AS EXTERNAL NAME
  RunningTotal.[RunningTotal.RunningTotalUtils].RunningTotal;
GO

And... the fastest ever running totals come with the following SQL query:

SQL
select *, dbo.RunningTotal(Amount) Total from Transactions;

This is correct for the following table:

SQL
create table Transactions
(
   ID int identity primary key,
   Amount decimal(18,2)
);

It seems no one can do it faster.

In future, Microsoft will probably add support for windowing and this article becomes obsolete. For instance, in PostgreSQL 8.4, we may do the following:

SQL
select *, sum(Amount) over(order by ID) Total from Transactions;

The result will be the same.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Russian Federation Russian Federation
I have started as a C++ developer in far 2000. I'd been developing banking and treasury software for 3 years using C++. In 2003 I switched to .NET at DELL. After that I worked as a project manager on different projects (internal audit, treasury automation, publishing house automation, etc.). Since 2009 I own a small software company specialized in SaaS services and develop a DotNetNuke modules.

Comments and Discussions

 
GeneralUsing WITH ROLLUP Pin
spoodygoon19-Jun-09 8:23
spoodygoon19-Jun-09 8:23 
GeneralRe: Using WITH ROLLUP [modified] Pin
Anton Burtsev20-Jun-09 0:24
Anton Burtsev20-Jun-09 0:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.