I am able to get a running total via script--but what I can't seem to do is isolate the line where I have met or exceeded a certain value...
SELECT
column1,
(SELECT SUM(column1) FROM table WHERE column2 <= t1.column2)
FROM
table t1
Ultimately what I would want to do is create a trigger to send dbmail when the Sum of column1 meets or exceeds (n)...help me obi-wan
On SQL-Server 2008 you can use next solution:
DECLARE @TBL TABLE(id int, amount int); INSERT INTO @TBL VALUES (1, 100), (2, 100), (3, 60), (4, 200), (5, 100); SELECT t1.ID, t1.amount, SUM(t2.amount) as CumTotal FROM @TBL t1 CROSS APPLY (SELECT * FROM @TBL WHERE ID <= t1.id) t2 GROUP BY t1.ID, t1.amount HAVING SUM(t1.amount) < 300 ORDER BY t1.ID ;
This is the result:
ID | amount | CumTotal -: | -----: | -------: 1 | 100 | 100 2 | 100 | 200 3 | 60 | 260
dbfiddle here
Although this solution works well, it is not recommendable on terms of performance.
In this case the best option is to use a CURSOR.
DECLARE @CS table (id int, amount int, total int); DECLARE @id int, @amount int; DECLARE @CumSum int = 0; DECLARE c CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY FOR SELECT ID, amount FROM @TBL ORDER BY [id]; OPEN c; FETCH NEXT FROM c INTO @id, @amount WHILE @@FETCH_STATUS = 0 AND @CumSum + @amount < 300 BEGIN SET @CumSum = @CumSum + @amount; INSERT @CS (id, amount, total) SELECT @id, @amount, @CumSum; FETCH NEXT FROM c INTO @id, @amount END CLOSE c; DEALLOCATE c; SELECT id, amount, total FROM @CS ORDER BY id;
GO
id | amount | total -: | -----: | ----: 1 | 100 | 100 2 | 100 | 200 3 | 60 | 260
dbfiddle here
Next answer can be used on SQL-SERVER 2012 and above
You can use a cumulative sum using a WINDOW function and SUM() ROWS UNBOUNDED PRECEDING.
Have a look at MS docs.
DECLARE @TBL TABLE(id int, amount int); INSERT INTO @TBL VALUES (1, 100), (2, 100), (3, 60), (4, 200), (5, 100);
Next query returns a cumulative sum:
SELECT ID, SUM(amount) OVER (ORDER BY ID ROWS UNBOUNDED PRECEDING) AS CumTotal FROM @TBL ;
ID | CumTotal -: | -------: 1 | 100 2 | 200 3 | 260 4 | 460 5 | 560
The problem is that you cannot stop it, you need to calculate all records, and then you can apply a where clause just to filter the records.
WITH CSum As ( SELECT ID, SUM(amount) OVER (ORDER BY ID ROWS UNBOUNDED PRECEDING) AS CumTotal FROM @TBL ) SELECT ID, CumTotal FROM CSum WHERE CumTotal < 300 ORDER BY ID ;
This is the final result:
ID | CumTotal -: | -------: 1 | 100 2 | 200 3 | 260
dbfiddle here