Recursive CTE with partition
-
10-03-2021 - |
Question
I have a table like this in MS SQL SERVER 2014:
ID|Race|Lap
1 |21 |11
2 |21 |NULL
3 |21 |NULL
4 |21 |NULL
5 |29 |65
6 |29 |NULL
7 |29 |NULL
8 |29 |NULL
I am trying to fill up the Lap column by adding 1 to it based on the first value. The partition is based on Race column. Something like this would be the end result:
ID|Race|Lap
1 |21 |11
2 |21 |12
3 |21 |13
4 |21 |14
5 |29 |65
6 |29 |66
7 |29 |67
8 |29 |68
There might be other ways of doing this but I would rather stick with recursive CTE. Is there any way to do this?
Solution
This would produce the expected result:
create table #demo (id int, race int, lap int)
insert into #demo values (1,21,11),(2,21,null),(3,21,null),(4,21,null),(5,29,65),(6,29,null),(7,29,null),(8,29,null);
with CTE as
(select race, ROW_NUMBER() over (partition by race order by race) "extra_lap" from #demo where lap is null),
CTE2 as
(select race, lap "lap" from #demo where lap is not null)
select race, lap from CTE2
union
select CTE.race, CTE2.lap + CTE.extra_lap "lap" from CTE join CTE2 on CTE.race=CTE2.race
drop table #demo;
OTHER TIPS
also another way using window functions :
create table #demo (id int, race int, lap int)
insert into #demo values (1,21,11),(2,21,null),(3,21,null),(4,21,null),(5,29,65),(6,29,null),(7,29,null),(8,29,null);
SELECT * , IIF(lap IS NULL , FIRST_VALUE(lap) OVER (PARTITION BY s.race ORDER BY id ) + RANK() OVER ( PARTITION BY s.race ORDER BY id ) -1 , lap)
FROM #demo AS s
For this kind of cases Recursive CTE can be used as below with no need to partition.
--This temp table created to store sample values
SELECT Id,Race,Lap
INTO #LapRecords
FROM (VALUES (1,21,11),(2,21,NULL),(3,21,NULL),(4,21,NULL),(5,29,65),(6,29,NULL),(7,29,NULL),(8,29,NULL)) LapRecords (Id,Race,Lap);
--Recursive CTE
WITH LapRecordsCTE (Id,Race,Lap) AS
(
SELECT Id,Race,Lap FROM #LapRecords WHERE Lap IS NOT NULL
UNION ALL
SELECT LapRecords.Id,LapRecords.Race,LapRecordsCTE.Lap+1
FROM #LapRecords AS LapRecords JOIN LapRecordsCTE
ON LapRecords.Id=LapRecordsCTE.Id+1 AND LapRecords.Race=LapRecordsCTE.Race
)
SELECT Id,Race,Lap FROM LapRecordsCTE ORDER BY Race,Lap;
--Temp table droped to clean up workspace
Drop Table #LapRecords
Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange