如何计算SQL中两个日期之间提取公共假日的工作日和小时数

时间:2016-04-02 03:25:46

标签: sql sql-server datediff getdate date-difference

嗨大家我是SQL的新手并陷入了一些复杂的查询。

我想要实现的目标是什么?

我想计算两个时间戳字段之间的两种类型的总天数。

  1. 工作天数(不包括周末及公众假期)
  2. 总天数(包括周末及公众假期)
  3. 计算条件

    • 如果OrderDate时间是< = 12:00 PM,则从0
    • 开始计数
    • 如果OrderDate时间是>中午12:00然后从-1

    • 开始计数
    • 如果交货日期为NULL,则计算不同,直到今天的日期

    数据模型

    • OrderDate & DeliveryDate 位于“ OrderTable
    • PublicHolidayDate 驻留' PublicHolidaysTable '

1 个答案:

答案 0 :(得分:2)

与SQL中的许多任务一样,这可以通过多种方式解决。

您可以使用BETWEEN运算符在日期范围内使用COUNT个汇总运算,以便为您提供从开始日期(OrderDate)到结束日期(DeliveryDate)的周末天数和假期的总计。

此功能可与CTE(公用表格式)结合使用,为您提供所需的最终结果集。

我已经汇总了一个查询,说明了你可以采取的一种方式。我还汇总了一些测试数据和结果,以说明查询的工作原理。

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为null,则要求DeliveryDate应使用今天的日期。

CTE报表末尾的select语句可获取每个订单的总日数,周末计数,假日计数和工作日。

;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语句转换成视图。

我认为对你来说特别有价值的观点可以让你在没有周末或假期的情况下工作。然后你可以简单地得到两个日期之间的日期差异,并计算相同范围内的工作日。