Search code examples
t-sqlquery-optimizationsql-server-2016

SQL - how to sum groups of 15 rows and find the max sum


The purpose of this question is to optimize some SQL by using set-based operations vs iterative (looping, like I'm doing below):

Some Explanation -

I have this cte that is inserted to a temp table #dataForPeak. Each row represents a minute, and a respective value retrieved.

For every row, my code uses a while loop to add 15 rows at a time (the current row + the next 14 rows). These sums are inserted into another temp table #PeakDemandIntervals, which is my workaround for then finding the max sum of these groups of 15.

I've bolded my end goal above. My code achieves this but in about 12 seconds for 26k rows. I'll be looking at much more data, so I know this is not enough for my use case.

My question is,

  • can anyone help me find a fast alternative to this loop?

It can include more tables, CTEs, nested queries, whatever. The while loop might not even be the issue, it's probably the inner code.

insert into #dataForPeak
    select timestamp, value
    from cte
    order by timestamp;

while @@ROWCOUNT<>0
begin
    declare @timestamp datetime = (select top 1 timestamp from #dataForPeak);
    insert into #PeakDemandIntervals
        select @timestamp, sum(interval.value) as peak
        from (select * from #dataForPeak base
              where base.timestamp >= @timestamp
              and base.timestamp < DATEADD(minute,14,@timestamp)
        ) interval;
    delete from #dataForPeak where timestamp = @timestamp;
end

select max(peak)
from #PeakDemandIntervals;

Edit

Here's an example of my goal, using groups of 3min instead of 15min. Given the data:

Time | Value
1:50 | 2
1:51 | 4
1:52 | 6
1:53 | 8
1:54 | 6
1:55 | 4
1:56 | 2

the max sum (peak) I'm looking for is 20, because the group

1:52 | 6
1:53 | 8
1:54 | 6

has the highest sum.

Let me know if I need to clarify more than that.


Solution

  • Based on the example given it seems like you are trying to get the maximum value of a rolling sum. You can calculate the 15-minute rolling sum very easily as follow:

    SELECT   [Time]
            ,[Value] 
            ,SUM([Value]) OVER (ORDER BY [Time] ASC ROWS 14 PRECEDING) [RollingSum]
    FROM    #dataForPeak
    

    Note the key here is the ROWS 14 PRECEDING statement. It effectively state that SQL Server should sum the preceding 14 records with the current record which will give you your 15 minute interval. Now you can simply max the result of the rolling sum. The full query will look as follow:

    ;WITH CTE_RollingSum
    AS
    (
        SELECT   [Time]
                ,[Value] 
                ,SUM([Value]) OVER (ORDER BY [Time] ASC ROWS 14 PRECEDING) [RollingSum]
        FROM    #dataForPeak
    )
    SELECT  MAX([RollingSum]) AS Peak
    FROM    CTE_RollingSum