SQL - 查询两个日期字段之间的日期列表
TSQL - Query a list of dates between two date fields
数据:
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' ;
目标:
输出2个字段。第一个 = 日期字段,第二个 = 位字段。日期字段将包含每条记录的 [WkStart] 和 [WkEnd] 之间的所有日期。当 [MyDate] 等于第一个字段的值时,位字段将为真。 table 非常大,因此性能很重要。当 2+ [MyDate] 值属于同一周范围时,该周的日期不应重复。
预期输出:
我的尝试:
; 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 ) ;
当前输出:
此解决方案有两个明显的问题。第一,记录重复属于同一日期范围内的 2 个以上日期(10 月 21 日和 10 月 23 日)。第二,这两个日期都以 2 个不同的位值重复,因此不能简单地使用 DISTINCT。第三个我觉得可能成为问题的是性能。也许有更有效的方法来实现这一点(也许使用函数)而不是使用递归 CTE。
当一周只有 7 天时,您不需要递归;只需将 7 个值 1-7 硬编码,这样您就可以使用这些值将集合从 WkStart
展开到 6 天后。然后您可以有条件地聚合 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;
数据:
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' ;
目标:
输出2个字段。第一个 = 日期字段,第二个 = 位字段。日期字段将包含每条记录的 [WkStart] 和 [WkEnd] 之间的所有日期。当 [MyDate] 等于第一个字段的值时,位字段将为真。 table 非常大,因此性能很重要。当 2+ [MyDate] 值属于同一周范围时,该周的日期不应重复。
预期输出:
我的尝试:
; 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 ) ;
当前输出:
此解决方案有两个明显的问题。第一,记录重复属于同一日期范围内的 2 个以上日期(10 月 21 日和 10 月 23 日)。第二,这两个日期都以 2 个不同的位值重复,因此不能简单地使用 DISTINCT。第三个我觉得可能成为问题的是性能。也许有更有效的方法来实现这一点(也许使用函数)而不是使用递归 CTE。
当一周只有 7 天时,您不需要递归;只需将 7 个值 1-7 硬编码,这样您就可以使用这些值将集合从 WkStart
展开到 6 天后。然后您可以有条件地聚合 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;