Data:
DECLARE @Dates TABLE
(
[MyDate] DATE
, [WkStart] DATE
, [WkEnd] DATE
) ;
INSERT INTO @Dates
SELECT '2021-10-03'
, '2021-09-27'
, '2021-10-03'
UNION
SELECT '2021-10-21'
, '2021-10-18'
, '2021-10-24'
UNION ALL
SELECT '2021-10-23'
, '2021-10-18'
, '2021-10-24'
UNION
SELECT '2021-10-27'
, '2021-10-25'
, '2021-10-31' ;
Goal:
To output 2 fields. 1st = a date field and 2nd = a bit field. Date field will have all the dates between [WkStart] and [WkEnd] for each record. Bit field will be true when [MyDate] equals the value of the first field. The table is quite large and so performance matters a lot. When 2 [MyDate] values belong to the same week range, the Dates for the week should not repeat.
Expected output:
My try:
; WITH recrCTE AS
(
SELECT CAST ( [WkStart] AS DATETIME ) AS [DateVal]
, [WkEnd]
, [MyDate]
FROM @Dates
UNION ALL
SELECT [DateVal] 1
, [WkEnd]
, [MyDate]
FROM recrCTE
WHERE [DateVal] 1 <= [WkEnd]
)
SELECT [DateVal]
, IIF ( [MyDate] = [DateVal], 1, 0 ) AS [isMyDate]
FROM recrCTE
ORDER BY [DateVal]
OPTION ( MAXRECURSION 0 ) ;
Current output:
Two obvious issues with this solution. 1st, records repeat for 2 dates that fall within same date range (21st Oct and 23rd Oct). 2nd, both of those dates repeat with 2 different bit values so can't simply use DISTINCT. 3rd that I feel might become an issue is performance. Maybe there's a more efficient way to achieve this (using a function perhaps) instead of using a recursive CTE.
CodePudding user response:
You don't need recursion when there can only be 7 days in a week; just hard-code the 7 values 1-7 so you can use those to explode the set from WkStart
to 6 days later. Then you can conditionally aggregate that output on DateVal
.
;WITH alldays AS
(
SELECT DateVal = DATEADD(DAY, days.n-1, d.WkStart),
d.MyDate
FROM @Dates AS d
CROSS JOIN (VALUES(1),(2),(3),(4),(5),(6),(7)) AS days(n)
-- if the table is large and performance is a concern, then:
-- WHERE d.? -- some reasonable where clause belongs here?
)
SELECT DateVal,
IsMyDate = MAX(CASE MyDate WHEN DateVal THEN 1 ELSE 0 END)
FROM alldays
GROUP BY DateVal
ORDER BY DateVal;
- Example db<>fiddle