Учитывая, что вы используете DATEFROMPARTS
, я предполагаю, что вы используете SQL 2012 или более поздней версии. Я также полагаюсь на вашу существующую логику в отношении определения первой и последней дат. Исходя из этого, замените ваш запрос следующим:
; WITH [Dates] AS (
SELECT
@StartDate AS [StartDate],
DATEADD(DAY, 6, @StartDate) AS [EndDate]
UNION ALL
SELECT
DATEADD(DAY, 7, [StartDate]),
DATEADD(DAY, 7, [EndDate])
FROM [Dates]
WHERE DATEADD(DAY, 7, [StartDate]) <= @EndDate
)
-- All weeks where all dates are within the same month
SELECT
StartDate
,EndDate
FROM [Dates]
WHERE MONTH(StartDate) = MONTH(EndDate)
-- For weeks where all dates not within the same month, the last week in the month
UNION ALL SELECT
StartDate
,EOMONTH(StartDate)
FROM [Dates]
WHERE MONTH(StartDate) <> MONTH(EndDate)
-- For weeks where all dates not within the same month, the first week in the (next) month
UNION ALL SELECT
DATEADD(dd, 1, EOMONTH(StartDate))
,EndDate
FROM [Dates]
WHERE MONTH(StartDate) <> MONTH(EndDate)
Профсоюзы делают это немного неудобно, но если вы не обрабатывать века с каждым проходить он будет работать достаточно быстро. Отметим также, что некоторые «недели» будут содержать один день, например, на 31 июля 2016 года по 31 июля 2016 года.
- Добавления на основе комментария ------------ ---------------------
следующий запрос делает это, но с большим предупреждением ...
; WITH cteDates AS (
SELECT
@StartDate AS StartDate,
DATEADD(DAY, 6, @StartDate) AS EndDate,
CASE
WHEN DATEPART(dw, EOMONTH(@StartDate)) between 2 and 6 then 0 -- Assumes SET DATEFIRST is 1!
ELSE 1
END AS MonthEndsOnWeekend
UNION ALL
SELECT
DATEADD(DAY, 7, StartDate),
DATEADD(DAY, 7, EndDate),
CASE
WHEN DATEPART(dw, EOMONTH(DATEADD(DAY, 7, StartDate))) between 2 and 6 then 0
ELSE 1
END
FROM cteDates
WHERE DATEADD(DAY, 7, StartDate) <= @EndDate
)
-- All weeks where all dates are within the same month,
-- and all month-ending weeks where the last day of the month is Saturday or Sunday
SELECT
StartDate
,EndDate
FROM cteDates
WHERE MONTH(StartDate) = MONTH(EndDate)
OR MonthEndsOnWeekend = 1
-- For weeks where all dates not within the same month,
-- and the the last day of the month is NOT Saturday or Sunday,
-- the last week in the month
UNION ALL SELECT
StartDate
,EOMONTH(StartDate)
FROM cteDates
WHERE MONTH(StartDate) <> MONTH(EndDate)
AND MonthEndsOnWeekend = 0
-- For weeks where all dates not within the same month,
-- and the the last day of the month is NOT Saturday or Sunday,
-- the first week in the (next) month
UNION ALL SELECT
DATEADD(dd, 1, EOMONTH(StartDate))
,EndDate
FROM cteDates
WHERE MONTH(StartDate) <> MONTH(EndDate)
AND MonthEndsOnWeekend = 0
Я использую функцию DATEPART
для определения дня недели (суббота, воскресенье и т. д.). SQL вернет номер для этой функции, где число, возвращаемое за день недели, зависит от установки SET DATEFIRST. Для моих установок мы используем значение по умолчанию: 1 = понедельник. Если у вас нет полного контроля над настройкой SET DATEFIRST во всем мире, ваш код может быть запущен, forever, тогда у вас могут возникнуть проблемы, выходящие за рамки этой дискуссии. Альтернативой, которую я использовал, является использование DATENAME
, который будет возвращать символьные строки, например. Субботу, воскресенье и т. Д., Но это имеет ту же проблему в отношении настройки SET LANGUAGE.
(fyi, я также вынул [] и переименовал цит, потому что они меня подслушивали.)
Какой РСУБД это? Добавьте тег, чтобы указать, используете ли вы 'mysql',' postgresql', 'sql-server',' oracle' или 'db2' - или что-то еще. –
Я вижу много ответов с объединением всех и нескольких cte и т. Д. Я знаю, что мои формулы немного менее просты в использовании, но это единственный рекурсивный cte. cheers – Matt