Home > Blockchain >  TSQL - Query a list of dates between two date fields
TSQL - Query a list of dates between two date fields

Time:10-22

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:

enter image description here

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:

enter image description here

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;
  • Related