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;