I’m using SQL Server, I have a table with 3 columns (timeseries) data
, with date, hour
beginning, AwardStatus
.
The award status for the most part is randomly generated. There can be two options, Awarded or Not Awarded.
However, the business requirement is that we MUST print ‘NotAwarded’ for 3 consecutive rows if the status is NotAwarded, and 4 consecutive rows if the status is Awarded.
Goal: a new column ShouldBe
details.
Once it meets the minimum requirements, then it checks that the current row’s AwardStatus
and continues to overwrite the logic.
Question: Is that possible in SQL without any kind of cursor/looping?
The picture in below as an example.
Here’s an example:
AwardStatusMinimum 3 AwardStatusMaximum 4 Date Hour AwardStatus ShouldBe -------------------------------------- 1/1/2019 1 NotAwarded NotAwarded 1/1/2019 2 NotAwarded NotAwarded 1/1/2019 3 Awarded NotAwarded 1/1/2019 4 Awarded Awarded 1/1/2019 5 NotAwarded Awarded 1/1/2019 6 NotAwarded Awarded 1/1/2019 7 Awarded Awarded 1/1/2019 8 NotAwarded NotAwarded 1/1/2019 9 Awarded NotAwarded 1/1/2019 10 Awarded NotAwarded
Advertisement
Answer
Since recursion was mentioned.
Here’s a solution that uses a recursive CTE.
Sample data:
CREATE TABLE Table1 ( [Date] DATETIME NOT NULL, [Hour] INT NOT NULL, [AwardStatus] VARCHAR(10) ); INSERT INTO Table1 ([Date], [Hour], [AwardStatus]) VALUES ('2019-01-01', 1, 'NotAwarded'), ('2019-01-01', 2, 'NotAwarded'), ('2019-01-01', 3, 'Awarded'), ('2019-01-01', 4, 'Awarded'), ('2019-01-01', 5, 'NotAwarded'), ('2019-01-01', 6, 'NotAwarded'), ('2019-01-01', 7, 'Awarded'), ('2019-01-01', 8, 'NotAwarded'), ('2019-01-01', 9, 'Awarded'), ('2019-01-01', 10, 'Awarded');
Query:
;with CTE_DATA AS ( select * , dense_rank() over (order by cast([Date] as date)) as grp , row_number() over (partition by cast([Date] as date) order by [Hour]) as rn from Table1 ) , RCTE_AWARDS as ( select [Date], [Hour] , AwardStatus , grp , rn , 1 as Lvl , AwardStatus AS CalcStatus from CTE_DATA where rn = 1 union all select t.[Date], t.[Hour] , t.AwardStatus , t.grp , t.rn , case when (c.lvl < 3) or (c.lvl < 4 and c.CalcStatus = 'Awarded') then c.lvl+1 else 1 end , case when (c.lvl = 3 and c.CalcStatus = 'NotAwarded') or (c.lvl = 4) then t.AwardStatus else c.CalcStatus end from RCTE_AWARDS c join CTE_DATA t on t.grp = c.grp and t.rn = c.rn + 1 ) select [Date], [Hour], AwardStatus , CalcStatus AS NewAwardStatus from RCTE_AWARDS order by [Date], [Hour] GODate | Hour | AwardStatus | NewAwardStatus :---------------------- | ---: | :---------- | :------------- 2019-01-01 00:00:00.000 | 1 | NotAwarded | NotAwarded 2019-01-01 00:00:00.000 | 2 | NotAwarded | NotAwarded 2019-01-01 00:00:00.000 | 3 | Awarded | NotAwarded 2019-01-01 00:00:00.000 | 4 | Awarded | Awarded 2019-01-01 00:00:00.000 | 5 | NotAwarded | Awarded 2019-01-01 00:00:00.000 | 6 | NotAwarded | Awarded 2019-01-01 00:00:00.000 | 7 | Awarded | Awarded 2019-01-01 00:00:00.000 | 8 | NotAwarded | NotAwarded 2019-01-01 00:00:00.000 | 9 | Awarded | NotAwarded 2019-01-01 00:00:00.000 | 10 | Awarded | NotAwarded
A test on db<>fiddle here