Я не думал, что этот код будет работать, и теперь, похоже, он работает, я беспокоюсь, что он не всегда может работать:
IF OBJECT_ID('tempdb..#Dates') IS NOT NULL DROP TABLE #Dates
; WITH Dates
AS (
SELECT CAST(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) AS DATE) AS CDate
UNION ALL
SELECT DATEADD(dd, 1, CDate) AS CDate
FROM Dates
WHERE DATEADD(dd, 1, CDate) < CAST(GETDATE() AS DATE)
)
SELECT d.CDate
, d.CDate AS BDate
INTO #Dates
FROM Dates d
OPTION (MAXRECURSION 400)
; WITH BDate
AS (
SELECT CDate
FROM #Dates d
WHERE CDate NOT IN ('2018-01-01', '2018-01-15', '2018-02-19') -- New Years, MLK Day, Presidents Day
AND DATEPART(dw, d.CDate) NOT IN (1,7)
)
UPDATE d
SET d.BDate = b.CDate
FROM #Dates d
JOIN BDate b
ON d.CDate <= b.CDate
SELECT * FROM #Dates
Что я не понимаю, так это как он знает, какое значение выбрать для оператора UPDATE. Если мы выберем случайную дату, например, 24 января, оператор SELECT даст нам несколько значений (поскольку это среда, и мы избегаем выходных, этот список будет включать 24, 25 и 26 января, а затем перейти к 29, 30, 31-му , так далее.). Так почему же он выбирает тот, который я на самом деле хочу (минимальное значение, которое соответствует критериям)? Конечно, мне не удастся использовать ORDER BY для принудительного оформления ордера.
Обычно я бы написал свой код, например, так:
IF OBJECT_ID('tempdb..#Dates') IS NOT NULL DROP TABLE #Dates
; WITH Dates
AS (
SELECT CAST(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) AS DATE) AS CDate
UNION ALL
SELECT DATEADD(dd, 1, CDate) AS CDate
FROM Dates
WHERE DATEADD(dd, 1, CDate) < CAST(GETDATE() AS DATE)
)
SELECT d.CDate
, d.CDate AS BDate
INTO #Dates
FROM Dates d
OPTION (MAXRECURSION 400)
; WITH BDate
AS (
SELECT CDate
FROM #Dates d
WHERE CDate NOT IN ('2018-01-01', '2018-01-15', '2018-02-19') -- New Years, MLK Day, Presidents Day
AND DATEPART(dw, d.CDate) NOT IN (1,7)
)
, BDMin
AS (
SELECT d.CDate
, MIN(b.CDate) AS BDate
FROM #Dates d
JOIN BDate b
ON d.CDate <= b.CDate
GROUP BY d.CDate
)
UPDATE d
SET d.BDate = b.BDate
FROM #Dates d
JOIN BDMin b
ON d.CDate = b.CDate
SELECT * FROM #Dates
Это, конечно, кажется безопаснее, но теперь мне интересно, если это необходимо.