A colleague of mine has a problem which I am trying to help him with.
He has a SQL view which contains the following data (Sample data):-
Category Value
Cat A 10
Cat A 20
Cat A 30
Cat B 15
Cat B 15
Cat C 10
Cat C 10
He wants to add a column to the view which keeps a running total of the Value column.
This column must reset the running total when the Category changes.
So the output data must be like this:-
Category Value Running
Cat A 10 10
Cat A 20 30
Cat A 30 60
Cat B 15 15
Cat B 15 30
Cat C 10 10
Cat C 10 20
We could get the running total by joining the table onto itself like this:-
select t1.id, t1.[count], SUM(t2.[count]) as sum
from TableA t1
inner join TableA t2 on t1.id >= t2.id
group by t1.id, t1.[count]
order by t1.id
Problem is we don't have an ID column and how would we instruct the running total to reset when the category changes ??
You could use the
ROW_NUMBER()
function combined with anOUTER APPLY
This will not perform particularly well on larger tables, but will do the job!
This works by using a Common table Expression (CTE) and windowed functions. The CTE works in a similar fashion to a subquery, where the first part (called running_total), adds a row-based ID to each class based on value.
Row_number() works as documented here but basically it auto-increments depending on how you define partitioning (essentially the same as group by, but without having to group in the main query) and an in-built order by class.
In this example partitionign by class ensures that each new category name will have the lowest value set to 1 - if you wanted a total running total not based on specific categories you would remove this part of the clause.
The second part of the CTE selects the results from the first part of the CTE and joins onto itself where classes match. By joining on r2.row <= r1.row this ensures that the second join includes all values <= the current row - i.e. Row 3 in R1 will include Row 1,2,3 in R2 join.