Skip to content
Advertisement

SQL Window Function – Number of Rows since last Max

I am trying to create a SQL query that will pull the number of rows since the last maximum value within a windows function over the last 5 rows. In the example below it would return 2 for row 8. The max value is 12 which is 2 rows from row 8.

For row 6 it would return 5 because the max value of 7 is 5 rows away.

|ID  | Date       | Amount  
| 1  | 1/1/2019   |  7  
| 2  | 1/2/2019   |  3  
| 3  | 1/3/2019   |  4  
| 4  | 1/4/2019   |  1  
| 5  | 1/5/2019   |  1  
| 6  | 1/6/2019   |  12  
| 7  | 1/7/2019   |  2  
| 8  | 1/8/2019   |  4  

I tried the following:

SELECT ID, date, MAX(amount) 
OVER (ORDER BY date ASC ROWS 5 PRECEDING) mymax
FROM tbl 

This gets me to the max values but I am unable to efficiently determine how many rows away it is. I was able to get close using multiple variables within the SELECT but this did not seem efficient or scalable.

Advertisement

Answer

You can calculate the cumulative maximum and then use row_number() on that.

So:

select t.*,
       row_number() over (partition by running_max order by date) as rows_since_last_max
from (select t.*, 
             max(amount) over (order by date rows between 5 preceding and current row) as running_max
      from tbl t
     ) t;

I think this works for your sample data. It might not work if you have duplicates.

In that case, you can use date arithmetic:

select t.*,
       datediff(day,
                max(date) over (partition by running_max order by date),
                date
               ) as days_since_most_recent_max5
from (select t.*, 
             max(amount) over (order by date rows between 5 preceding and current row) as running_max
      from tbl t
     ) t;

EDIT:

Here is an example using row number:

select t.*,
       (seqnum - max(case when amount = running_amount then seqnum end) over (partition by running_max order by date)) as rows_since_most_recent_max5
from (select t.*, 
             max(amount) over (order by date rows between 5 preceding and current row) as running_max,
             row_number() over (order by date) as seqnum
      from tbl t
     ) t;
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement