Количество дней недели в диапазоне дат в TSQL - PullRequest
4 голосов
/ 15 июля 2011

Это сложнее, чем кажется.Мне нужна функция, которая вычисляет номера данного дня недели в диапазоне дат.Я не хочу никаких циклов или рекурсивного sql, есть миллионы примеров, делающих именно это, мне нужна быстрая функция для расчета.

Ввод функции будет: dayday, fromdata, todate

-- counting fridays
set datefirst 1
SELECT dbo.f_countweekdays(5, '2011-07-01', '2011-07-31'),
dbo.f_countweekdays(5, '2011-07-08', '2011-07-15'),
dbo.f_countweekdays(5, '2011-07-09', '2011-07-15'),
dbo.f_countweekdays(5, '2011-07-09', '2011-07-14')

ожидаемый результат:

5, 2, 1, 0

Я надеюсь, что кто-то может помочь.

Ответы [ 4 ]

4 голосов
/ 15 июля 2011
create function dbo.f_countweekdays
(
  @DOW int, 
  @StartDate datetime, 
  @EndDate datetime
) 
returns int
begin
  return
  ( select datediff(wk, T2.St, T2.En) -
           case when T1.SDOW > @DOW then 1 else 0 end -
           case when T1.EDOW < @DOW then 1 else 0 end
    from (select datepart(dw, @StartDate),
                 datepart(dw, @EndDate)) as T1(SDOW, EDOW)
      cross apply (select dateadd(d, - T1.SDOW, @StartDate),
                          dateadd(d, 7 - T1.EDOW, @EndDate)) as T2(St, En))
end
3 голосов
/ 15 июля 2011

@ Микаэль Эрикссон получил замечательную идею, но его реализация кажется несколько усложненной.

Вот то, что я придумал (и я хотел бы подчеркнуть, что это основано на решение от @ Mikael , которому должен идти основной кредит):

ALTER FUNCTION dbo.f_countweekdays (@Dow int, @StartDate datetime, @EndDate datetime)
RETURNS int
AS BEGIN
  RETURN (
    SELECT
      DATEDIFF(wk, @StartDate, @EndDate)
      - CASE WHEN DATEPART(dw, @StartDate) > @Dow THEN 1 ELSE 0 END
      - CASE WHEN DATEPART(dw, @EndDate)   < @Dow THEN 1 ELSE 0 END
      + 1
  )
END

ОБНОВЛЕНИЕ

AsМикаэль правильно отметил в ветке комментариев своего ответа, чтобы вышеуказанное решение работало правильно, для параметра DATEFIRST должно быть установлено значение 7 (воскресенье).Хотя я не смог найти это документированное, быстрый тест показал, что DATEDIFF(wk) не учитывает фактическую настройку DATEFIRST и действительно возвращает разницу в неделях, как если бы DATEFIRST всегда был установлен на 7. В то же время DATEPART(dw) уважает DATEFIRSTтаким образом, если для параметра DATEFIRST установлено значение, отличное от 7, две функции возвращают взаимно несовместимые результаты.

Поэтому в приведенный выше сценарий необходимо внести поправки, чтобы учесть различные значения параметра DATEFIRST при расчете DATEDIFF(wk).К счастью, исправление, по-моему, не сделало решение намного более сложным, чем раньше, на мой взгляд.Судите сами, хотя:

ALTER FUNCTION dbo.f_countweekdays (@Dow int, @StartDate datetime, @EndDate datetime)
RETURNS int
AS BEGIN
  RETURN (
    SELECT
      DATEDIFF(wk, DATEADD(DAY, -@@DATEFIRST, @StartDate),
                   DATEADD(DAY, -@@DATEFIRST, @EndDate))
      - CASE WHEN DATEPART(dw, @StartDate) > @Dow THEN 1 ELSE 0 END
      - CASE WHEN DATEPART(dw, @EndDate)   < @Dow THEN 1 ELSE 0 END
      + 1
  )
END

Отредактировано: Обе -@@DATEFIRST % 7 записи были упрощены до -@@DATEFIRST, как кто-то предложил здесь .

2 голосов
/ 15 июля 2011

Альтернативный подход - это старомодное измерение времени хранилища данных, в котором у вас есть таблица со всеми потенциальными датами, а также любая полезная информация, которую вы хотите отфильтровать / подсчитать по:

Key       ActualDate  DayName   IsWeekday  DayNumberInYear  FinancialQuarter
20110101  1 Jan 2011  Saturday  0          1                2011 Q1
20110102  2 Jan 2011  Sunday    0          2                2011 Q1
20110103  3 Jan 2011  Monday    1          3                2011 Q1

Затем просто присоединитесь к этой таблице и отфильтруйте, например,

SELECT 
  COUNT(*) 
FROM 
  date_dimension
WHERE
  ActualDate BETWEEN '1 Jan 2011' AND '3 Jan 2011' AND
  IsWeekday = 1

Если вы выполняете анализ дат за известный диапазон дат , это действительно может ускорить и упростить ваши запросы. Если вы заранее знаете, какие диапазоны дат возможны, это ограничивающий фактор того, действительно ли это полезно, но это полезный прием, о котором нужно знать.

1 голос
/ 15 июля 2011

Это то, что я придумал после того, как попробовал много разных подходов.Я потратил много времени на его решение, и я все еще работал над ним, когда я разместил вопрос.Я решил опубликовать его как ответ из-за значка самообучения, хотя я никогда не получал более 2 баллов за ответ.

alter function dbo.f_countweekdays 
( @day int, @fromdate datetime, @todate datetime )  
returns int 
begin 
RETURN (SELECT datediff(day, @fromdate, dateadd(week,datediff(week,0,@todate - 1) + 
CASE WHEN datepart(weekday,@todate) < @day THEN 0 ELSE 1 END,0) + @day - 1) / 7)
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...