Получить количество дней недели (воскресенье, понедельник, вторник) между двумя датами SQL

Итак, я хотел бы, чтобы в качестве даты начала и окончания определить, сколько конкретных дней недели происходит между этими двумя датами.

Так сколько же понедельников, вторников и т. Д.

Я знаю, что могу сделать это с помощью цикла между начальной и конечной датой и проверкой каждый день, но разница может составить большое количество дней. Я бы предпочел что-то, что не требует петли. Есть идеи? (Должно поддерживаться в SQL Server 2005+)

7 ответов

Решение

Учитывая то, что я думаю, вы пытаетесь получить, это должно сделать это:

SET DATEFIRST 1

DECLARE
    @start_date DATETIME,
    @end_date DATETIME

SET @start_date = '2011-07-11'
SET @end_date = '2011-07-22'

;WITH Days_Of_The_Week AS (
    SELECT 1 AS day_number, 'Monday' AS day_name UNION ALL
    SELECT 2 AS day_number, 'Tuesday' AS day_name UNION ALL
    SELECT 3 AS day_number, 'Wednesday' AS day_name UNION ALL
    SELECT 4 AS day_number, 'Thursday' AS day_name UNION ALL
    SELECT 5 AS day_number, 'Friday' AS day_name UNION ALL
    SELECT 6 AS day_number, 'Saturday' AS day_name UNION ALL
    SELECT 7 AS day_number, 'Sunday' AS day_name
)
SELECT
    day_name,
    1 + DATEDIFF(wk, @start_date, @end_date) -
        CASE WHEN DATEPART(weekday, @start_date) > day_number THEN 1 ELSE 0 END -
        CASE WHEN DATEPART(weekday, @end_date)   < day_number THEN 1 ELSE 0 END
FROM
    Days_Of_The_Week

Я не уверен, что после ОП, это будет подсчитывать в день недели:

SET DATEFIRST 1
DECLARE @StartDate datetime
       ,@EndDate datetime
SELECT @StartDate='7/13/2011'
      ,@EndDate='7/28/2011'
;with AllDates AS
(
    SELECT @StartDate AS DateOf, datename(weekday,@StartDate) AS WeekDayName, datepart(weekday,@StartDate) AS WeekDayNumber
    UNION ALL
    SELECT DateOf+1, datename(weekday,DateOf+1), datepart(weekday,DateOf+1)
        FROM AllDates
    WHERE DateOf<@EndDate
)
SELECT COUNT(*) CountOf,WeekDayName  FROM AllDates GROUP BY WeekDayName,WeekDayNumber ORDER BY WeekDayNumber

ВЫХОД:

CountOf     WeekDayName
----------- ------------------------------
2           Monday
2           Tuesday
3           Wednesday
3           Thursday
2           Friday
2           Saturday
2           Sunday

(7 row(s) affected)

это даст счет дней с понедельника по пятницу:

SET DATEFIRST 1
DECLARE @StartDate datetime
       ,@EndDate datetime
SELECT @StartDate='7/13/2011'
      ,@EndDate='7/28/2011'
;with AllDates AS
(
    SELECT @StartDate AS DateOf, datepart(weekday,@StartDate) AS WeekDayNumber
    UNION ALL
    SELECT DateOf+1, datepart(weekday,DateOf+1)
        FROM AllDates
    WHERE DateOf<@EndDate
)
SELECT COUNT(*) AS WeekDayCount FROM AllDates WHERE WeekDayNumber<=5

ВЫХОД:

WeekDayCount
------------
12

(1 row(s) affected)

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

SET DATEFIRST 1
DECLARE @StartDate datetime
       ,@EndDate datetime
SELECT @StartDate='7/13/2011'
      ,@EndDate='7/28/2011'
;with AllDates AS
(
    SELECT @StartDate AS DateOf, datepart(weekday,getdate()) AS WeekDayNumber
    UNION ALL
    SELECT DateOf+1, (WeekDayNumber+1) % 7
        FROM AllDates
    WHERE DateOf<@EndDate
)
SELECT COUNT(*) AS WeekDayCount FROM AllDates WHERE WeekDayNumber>0 AND WeekDayNumber<6
--I don't like using "BETWEEN", ">", ">=", "<", and "<=" are more explicit in defining end points

производит тот же вывод, что и исходный запрос.

Это предполагает стандартные настройки, но может быть адаптировано

DECLARE @StartDate datetime, @EndDate datetime
SELECT @StartDate='20110601', @EndDate='20110630'

;WITH AllDates AS
(
    SELECT @StartDate AS DateOf, datepart(weekday, @StartDate) AS WeekDayNumber
    UNION ALL
    SELECT DateOf+1, datepart(weekday, DateOf+1) 
    FROM AllDates
    WHERE DateOf < @EndDate
)
SELECT SUM(CASE WHEN WeekDayNumber BETWEEN 2 AND 6 THEN 1 ELSE 0 END) AS WeekDayCount
FROM AllDates
OPTION (MAXRECURSION 0)

Это должно быть допустимо для SQL Server и должно быть безопасным для международных отношений (примечание: у меня нет сервера для проверки этого).

SELECT datediff(day, @start, @end) - datediff(week, @start, @end) * 2 
                                   - CASE WHEN datepart(weekday, @start) 
                                               IN (datepart(weekday, '1970-01-03'), 
                                                   datepart(weekday, '1970-01-04')) 
                                          THEN 1
                                          ELSE 0 END, 
                                   - CASE WHEN datepart(weekday, @end) 
                                               IN (datepart(weekday, '1970-01-03'), 
                                                   datepart(weekday, '1970-01-04')) 
                                          THEN 1
                                          ELSE 0 END

Дайте этому вихрь.


Учитывая уточнение, это должно получить число каждого из дней.
Не использует рекурсию и должен быть полностью безопасным для международного сообщества. При необходимости вам придется настроить параметры даты начала / окончания для включения / исключения (версия DB2, которую я использовал для проверки этого, исключала дату начала, но включала, например, дату окончания).

WITH dayOfWeek (name, dayNumber) as (VALUES(dayname(weekday, '1970-01-01'), daypart(weekday, '1970-01-01')), 
                                           (dayname(weekday, '1970-01-02'), daypart(weekday, '1970-01-02')), 
                                           (dayname(weekday, '1970-01-03'), daypart(weekday, '1970-01-03')), 
                                           (dayname(weekday, '1970-01-04'), daypart(weekday, '1970-01-04')), 
                                           (dayname(weekday, '1970-01-05'), daypart(weekday, '1970-01-05')), 
                                           (dayname(weekday, '1970-01-06'), daypart(weekday, '1970-01-06')), 
                                           (dayname(weekday, '1970-01-07'), daypart(weekday, '1970-01-07')))
SELECT name, dayNumber, datediff(weeks, @start, @end)
                        + CASE WHEN datepart(weekday, @end) >= dayNumber THEN 1 ELSE 0 END
                        - CASE WHEN datepart(weekday, @start) >= dayNumber THEN 1 ELSE 0 END
FROM dayOfWeek

Это помогает?

Я бы просто добавил в качестве комментария к помеченному ответу, но не хватило бы "репутации". Вместо того, чтобы жестко задавать число day_number, которое зависит от datefirst, вы можете установить его, обнаружив день недели для каждого дня недели:

;WITH Days_Of_The_Week AS (
        SELECT DATEPART(dw, '2007-01-01') AS day_number, 'Monday' AS day_name UNION ALL -- 2007-01-01 is a known Monday
        SELECT DATEPART(dw, '2007-01-02') AS day_number, 'Tuesday' AS day_name UNION ALL
        SELECT DATEPART(dw, '2007-01-03') AS day_number, 'Wednesday' AS day_name UNION ALL
        SELECT DATEPART(dw, '2007-01-04') AS day_number, 'Thursday' AS day_name UNION ALL
        SELECT DATEPART(dw, '2007-01-05') AS day_number, 'Friday' AS day_name UNION ALL
        SELECT DATEPART(dw, '2007-01-06') AS day_number, 'Saturday' AS day_name UNION ALL
        SELECT DATEPART(dw, '2007-01-07') AS day_number, 'Sunday' AS day_name
    )
@start_date date = '2017-08-11',    
@end_date date = '2017-08-27',    
@weekday int = 7,    
@count int output    

As    

Begin    

Declare @i int = 0
set @count = 0
while(@i <= (select Datediff(Day, @start_date, @end_date)))
begin
    if(Dateadd(Day, @i, @start_date) > @end_date)
        break
    if(Datepart(weekday, Dateadd(Day, @i, @start_date)) = @weekday)
        set @count += 1
    set @i += 1
end
select @count

Ты можешь использовать DATEDIFF а также DATEPART функции + некоторая базовая математика, чтобы получить желаемый результат без зацикливания.

Другие вопросы по тегам