2016-04-02 3 views
2

Привет, ребята Я новичок в SQL и застрял в некотором сложном запросе.Как подсчитать количество рабочих дней и часов, выделяющих государственные праздники между двумя датами в SQL

Что я пытаюсь достичь?

Я хочу рассчитать следующие два типа общих дней между двумя полями времени.

  1. Количество рабочих дней (исключая выходные & праздничные дни)
  2. Количество Всего дней (включая выходные & праздничные дни)

Состояние Расчет

  • Если OrderDate время is < = 12:00 PM Затем начните с 0
  • Если OrderDate Время> 12:00 вечера затем начать отсчет от -1

  • Если дата поставки NULL, то подсчитывать разные до сегодняшней даты

Data Model

  • Номер заказа & DeliveryDate проживает в ' OrderTable '
  • PublicHolidayDate живет' PublicHolidaysTable '
+0

Показывать нам, что вы пробовали до сих пор, будет полезно. –

ответ

1

Как и многие другие задачи в SQL, это может быть решена несколькими способами.

Вы можете использовать агрегированные операции COUNT в диапазоне дат с оператором BETWEEN, чтобы получить итоговые суммы дней выходных дней и праздничных дней с даты начала (OrderDate) до даты окончания (DeliveryDate).

Эта функциональность может быть объединена с CTE (Common Table Expressions), чтобы дать вам набор конечных результатов, который вы ищете.

Я собрал запрос, который иллюстрирует один способ, которым вы могли бы это сделать. Я также собрал некоторые тестовые данные и результаты, чтобы проиллюстрировать, как работает запрос.

DECLARE @DateRangeBegin DATETIME = '2016-01-01' 
     , @DateRangeEnd DATETIME = '2016-07-01' 

DECLARE @OrderTable TABLE 
(OrderNum INT, OrderDate DATETIME, DeliveryDate DATETIME) 

INSERT INTO @OrderTable VALUES 
    (1, '2016-02-12 09:30', '2016-03-01 13:00') 
, (2, '2016-03-15 13:00', '2016-03-30 13:00') 
, (3, '2016-03-22 14:00', NULL) 
, (4, '2016-05-06 10:00', '2016-05-19 13:00') 

DECLARE @PublicHolidaysTable TABLE 
(PublicHolidayDate DATETIME, Description NVARCHAR(255)) 

INSERT INTO @PublicHolidaysTable VALUES 
    ('2016-02-15', 'President''s Day') 
, ('2016-03-17', 'St. Patrick''s Day') 
, ('2016-03-25', 'Good Friday') 
, ('2016-03-27', 'Easter Sunday') 
, ('2016-05-05', 'Cinco de Mayo') 

Некоторые соображения вы можете уже думали о том, что вы не хотите, чтобы рассчитывать как в выходные дни и праздники, которые происходят в выходные дни, если ваша компания не соблюдает праздник на следующий понедельник. Для простоты я исключил любой праздник, который происходит в выходной день в запросе.

Вы также хотите ограничить этот тип запросов конкретным диапазоном дат.

Первый CTE (cteAllDates) получает все даты между диапазоном начала и окончания даты.

Второй CTE (cteWeekendDates) получает все выходные от первого CTE (cteAllDates).

Третий CTE (ctePublicHolidays) получает все праздники, которые происходят в будние дни с вашего сайта PublicHolidaysTable.

Последний CTE (cteOrders) выполняет требование о том, что общее количество дней и рабочих дней должно начинаться со следующего дня, если OrderDate находится после 12:00 PM, и требование, чтобы DeliveryDate использовала сегодняшнюю дату, если она равна нулю ,

Оператор select в конце заявлений CTE получает ваш общий счет дня, количество выходных дней, количество праздников и рабочие дни для каждого заказа.

;WITH cteAllDates AS (
SELECT 1 [DayID] 
    , @DateRangeBegin [CalendarDate] 
    , DATENAME(dw, @DateRangeBegin) [NameOfDay] 
UNION ALL 
SELECT cteAllDates.DayID + 1 [DayID] 
    , DATEADD(dd, 1 ,cteAllDates.CalendarDate) [CalenderDate] 
    , DATENAME(dw, DATEADD(d, 1 ,cteAllDates.CalendarDate)) [NameOfDay] 
FROM cteAllDates 
WHERE DATEADD(d,1,cteAllDates.CalendarDate) < @DateRangeEnd 
) 
, cteWeekendDates AS (
SELECT CalendarDate 
FROM cteAllDates 
WHERE NameOfDay IN ('Saturday','Sunday') 
) 
, ctePublicHolidays AS (
SELECT PublicHolidayDate 
FROM @PublicHolidaysTable 
WHERE DATENAME(dw, PublicHolidayDate) NOT IN ('Saturday', 'Sunday') 
) 
, cteOrders AS (
SELECT OrderNum 
    , OrderDate 
    , CASE WHEN DATEPART(hh, OrderDate) >= 12 THEN DATEADD(dd, 1, OrderDate) 
      ELSE OrderDate 
     END [AdjustedOrderDate] 
    , CASE WHEN DeliveryDate IS NOT NULL THEN DeliveryDate 
      ELSE GETDATE() 
     END [DeliveryDate] 
FROM @OrderTable 
) 
SELECT o.OrderNum 
    , o.OrderDate 
    , o.DeliveryDate 
    , DATEDIFF(DAY, o.AdjustedOrderDate, o.DeliveryDate) [TotalDayCount] 
    , (SELECT COUNT(*) FROM cteWeekendDates w 
     WHERE w.CalendarDate BETWEEN o.AdjustedOrderDate AND o.DeliveryDate) [WeekendDayCount] 
    , (SELECT COUNT(*) FROM ctePublicHolidays h 
     WHERE h.PublicHolidayDate BETWEEN o.AdjustedOrderDate AND o.DeliveryDate) [HolidayCount] 
    , DATEDIFF(DAY, o.AdjustedOrderDate, o.DeliveryDate) 
     - (SELECT COUNT(*) FROM cteWeekendDays w 
      WHERE w.CalendarDate BETWEEN o.AdjustedOrderDate AND o.DeliveryDate) 
     - (SELECT COUNT(*) FROM ctePublicHolidays h 
      WHERE h.PublicHolidayDate BETWEEN o.AdjustedOrderDate AND o.DeliveryDate) [WorkingDays] 
FROM cteOrders o 
WHERE o.OrderDate BETWEEN @DateRangeBegin AND @DateRangeEnd 
OPTION (MaxRecursion 500) 

Результаты вышеупомянутого запроса с использованием тестовых данных ...

Query Results

То, что я бы, вероятно, сделать, это упростить выше, добавив таблицу календаря, населенную с достаточно широким диапазоном дат. Затем я бы взял некоторые из заявлений CTE и превратил их в представления.

Я думаю, что особенно ценным для вас было бы представление, которое даст вам рабочие дни без выходных или праздничных дней. Тогда вы можете просто получить разницу дат между двумя датами и подсчитать рабочие дни в том же диапазоне.

 Смежные вопросы

  • Нет связанных вопросов^_^