Я тоже брошу свою шляпу на ринг. : -)
DECLARE @Date datetime = '01/11/2015'
DECLARE @StartDate datetime = DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date - 6) % 7)), @Date - 6) -- MONDAY
DECLARE @EndDate datetime = DATEADD(d, (5 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date - 6) % 7)), @Date - 6) -- FRIDAY
SELECT '@Date' as Variable ,CONVERT(date, @Date) as DateValue ,DATENAME(dw, @Date) as DayOfTheWeek
UNION SELECT '@StartDate' as Variable ,CONVERT(date, @StartDate) as DateValue ,DATENAME(dw, @StartDate) as DayOfTheWeek
UNION SELECT '@EndDate' as Variable ,CONVERT(date, @EndDate) as DateValue ,DATENAME(dw, @EndDate) as DayOfTheWeek
-- Variable DateValue DayOfTheWeek
-- ---------- ---------- ------------
-- @Date 2015-01-11 Sunday
-- @StartDate 2015-01-05 Monday
-- @EndDate 2015-01-09 Friday
БОНУС: Здесь вы можете создать быстрый стол из 5 рабочих дней, используя ту же технику.
SELECT DATENAME(dw, DATEADD(d, TT.DaysToAdd, DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date - 6) % 7)), @Date - 6))) as DayOfTheWeek
, DATEADD(d, TT.DaysToAdd, DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date - 6) % 7)), @Date - 6)) as DateValue
FROM (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 as DaysToAdd FROM (VALUES(0),(0),(0),(0),(0)) a(n)) as TT
-- DayOfTheWeek DateValue
-- ------------------------------ -----------------------
-- Monday 2015-01-05 00:00:00.000
-- Tuesday 2015-01-06 00:00:00.000
-- Wednesday 2015-01-07 00:00:00.000
-- Thursday 2015-01-08 00:00:00.000
-- Friday 2015-01-09 00:00:00.000
Вот объяснение:
1) Сначала нам нужно узнать, с какой даты начать нашу оценку. Для этого примера мы решили использовать воскресенье, 11 января 2015 года.
DECLARE @Date2 datetime = '01/11/2015'
1b) Приятно иметь бонусную технику для получения названия дня недели с учетом значения даты
SELECT @Date2 as DateValue, DATENAME(dw, @Date2) as DayOfTheWeek
2) Затем нам нужно детерминистически (по американскому календарю) узнать, какой день недели численно находится между 1 и 7
- ПРИМЕЧАНИЕ. 1899.12.31 - это первое воскресенье до 1900.01.01, которое является МИНИМАЛЬНЫМ значением для типа данных SmallDateTime.
- ПРИМЕЧАНИЕ: Да, вы можете использовать DATEPART (dw, @Date), как это проще, но это не является детерминированным, учитывая, что определенные серверные среды могут иметь разные конфигурации
РЕЗУЛЬТАТЫ: 1 = воскресенье | 2 = понедельник | 3 = вторник | 4 = среда | 5 = четверг | 6 = пятница | 7 = суббота
SELECT ((DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date2) % 7) + 1) [DayOfWeek Deterministic (Based on US)]
3) Теперь, учитывая любую дату, вы должны иметь детерминистский способ определения понедельника для данной недели
SELECT DATEADD(d, (1 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date2) % 7)), @Date2) as [Monday Day of the Week - Deterministic (Based on US)]
4) Теперь, учитывая любую дату, вы должны иметь детерминистический способ определения пятницы для данной недели
SELECT DATEADD(d, (5 - (DATEDIFF(d, CAST('1899.12.31' AS datetime), @Date2) % 7)), @Date2) as [Monday Day of the Week - Deterministic (Based on US)]
5) Последний метод манипулирования датами, который нам нужен, - это знать, как попасть в неделю, в которой первая полная неделя будних дней происходит раньше. Например, если мы находимся в воскресенье и вычитаем из него 1 день, то мы попадаем в субботу, которая ставит нас на предыдущую неделю, которая является первой полной неделей в будние дни. В качестве альтернативы, если мы также вычтем 1 день из понедельника, это приведет нас только к воскресенью, которое не является предыдущей неделей, поэтому вычитания 1 дня недостаточно. С другой стороны, если бы мы были в субботу и вычли 7 дней, это перенесло бы нас за предыдущую полную неделю буднего на предыдущую неделю, что слишком далеко. Вот краткий анализ, чтобы выяснить, какие магические числа вы можете вычесть, которые будут работать с любым днем недели. Как вы можете видеть ниже, магическое число - 6.
-- DAYS TO SUBTRACT
-- Day of the Week - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7
-- =============== ==== ==== ==== ==== ==== ==== ==== ====
-- Sunday Bad Good Good Good Good Good Good Good
-- Monday Bad Bad Good Good Good Good Good Good
-- Tuesday Bad Bad Bad Good Good Good Good Good
-- Wednesday Bad Bad Bad Bad Good Good Good Good
-- Thursday Bad Bad Bad Bad Bad Good Good Good
-- Friday Bad Bad Bad Bad Bad Bad Good Good
-- Saturday Good Good Good Good Good Good Good Bad
БОНУС) Если вы хотите, чтобы все рабочие дни были в маленьком столике, то вам также следует использовать "таблицу подсчета", основанную на быстром нулевом значении. Есть много способов сделать это, поэтому выберите свой вкус. Вот несколько.
SELECT * FROM (SELECT 0 as DaysToAdd UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) as TT
SELECT * FROM (SELECT TOP 5 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 as DaysToAdd FROM sys.all_columns a CROSS JOIN sys.all_columns b) as TT
SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 as DaysToAdd FROM (VALUES(0),(0),(0),(0),(0)) a(n)) as TT