Here is one method that avoids self-joins and complex LEAD/LAG
expressions.
It would work with simple data types like int
, date
, decimal
. With float
you need to be careful to not lose precision when converting to/from the text. I would argue that price should never be stored as float
, I prefer money
type to store this kind of data.
Idea:
Combine values of two columns into a single column that can be used in a rolling MAX
function and then split them back.
Here I combine them into an ordinary text varchar
. If you use int
and date
you can combine into varbinary
, which may be a bit more efficient.
REPLACE(STR(price, 20, 0), ' ', '0') + CONVERT(varchar(10), session_date, 126) AS combined
Convert price
into a string, make sure you make it left-padded with zeros, so that it is sorted properly.
Convert session_date
into a string and attach it to the price
.
Later split this string back into corresponding values.
Sample data
DECLARE @my_table TABLE (
session_date date,
price decimal(15,2)
);
insert into @my_table values
('2022-12-31', 4),
('2022-12-30', 4),
('2022-12-29', 9),
('2022-12-28', 7),
('2022-12-27', 2),
('2022-12-26', 4),
('2022-12-25', 3),
('2022-12-24', 1);
Query
WITH
CTE
AS
(
SELECT
*
,max(combined) over (order by session_date DESC ROWS BETWEEN CURRENT ROW AND 3 FOLLOWING) as rolling_combined
FROM
@my_table AS T
CROSS APPLY
(
SELECT
REPLACE(STR(price, 20, 0), ' ', '0') + CONVERT(varchar(10), session_date, 126) AS combined
) AS A
)
SELECT
session_date
,price
,CAST(LEFT(rolling_combined, 20) AS decimal(15,2)) AS rolling_price
,CAST(RIGHT(rolling_combined, 10) as date) AS rolling_date
,rolling_combined
FROM CTE
ORDER BY session_date DESC;
It is important to have the same ORDER BY
in the main query and in the MAX
to avoid second sort.
Result
I included the combined column in the output to show how it looks like.
+--------------+-------+---------------+--------------+--------------------------------+
| session_date | price | rolling_price | rolling_date | rolling_combined |
+--------------+-------+---------------+--------------+--------------------------------+
| 2022-12-31 | 4.00 | 9.00 | 2022-12-29 | 000000000000000000092022-12-29 |
| 2022-12-30 | 4.00 | 9.00 | 2022-12-29 | 000000000000000000092022-12-29 |
| 2022-12-29 | 9.00 | 9.00 | 2022-12-29 | 000000000000000000092022-12-29 |
| 2022-12-28 | 7.00 | 7.00 | 2022-12-28 | 000000000000000000072022-12-28 |
| 2022-12-27 | 2.00 | 4.00 | 2022-12-26 | 000000000000000000042022-12-26 |
| 2022-12-26 | 4.00 | 4.00 | 2022-12-26 | 000000000000000000042022-12-26 |
| 2022-12-25 | 3.00 | 3.00 | 2022-12-25 | 000000000000000000032022-12-25 |
| 2022-12-24 | 1.00 | 1.00 | 2022-12-24 | 000000000000000000012022-12-24 |
+--------------+-------+---------------+--------------+--------------------------------+