如何在sql server查询中排除周末和假日

时间:2016-03-15 20:31:14

标签: sql-server date

我正在创建一个调整日期和周末的日期。

示例数据:

Adjusted Date | Adjusted Date(Excluding Holidays and weekends)

02/06/16 | 02/09/16

在我的例子中,日期是一个周末,调整日期变为feb 9因为feb 8是假日,所以需要调整以使调整后的日期成为工作日。目前,我有一个财政年度所有周末和假期的分隔表。

select  case when (
               select   count(dbo.WeekendsHoliday.[Weekends & Holidays])
               from     dbo.WeekendsHoliday
               where    dbo.WeekendsHoliday.[Weekends & Holidays] 
                    = case when convert(time, [Time Received]) > convert(time, '5:00:00 PM') 
                           then dateadd(day, 1, [Date Received]) 
                           else [Date Received] 
                      end
              ) > 0 
         then case (datename(DW, 
                             case when convert(time, [Time Received]) > convert(time, '5:00:00 PM') 
                                  then dateadd(day, 1, [Date Received])
                                  else [Date Received] 
                             end))
                when 'Saturday'
                then dateadd(day, 2, 
                             case when convert(time, [Time Received]) > convert(time, '5:00:00 PM') 
                                  then dateadd(day, 1, [Date Received])
                                  else [Date Received] 
                             end)
                else dateadd(day, 1, 
                             case when convert(time, [Time Received]) > convert(time, '5:00:00 PM') 
                                  then dateadd(day, 1, [Date Received])
                                  else [Date Received] 
                             end)
              end
    end as [Adjusted Date Excluding holidays and weekends]

这里发生的事情是假期是连续2天(星期四和星期五),调整日期是星期六,因为这是一个周末仍然无效。

调整后的日期是此处的别名

我真的需要你的帮助。感谢

4 个答案:

答案 0 :(得分:3)

这将获取您的WeekendsHoliday表中的日期,并查找不在同一个表中的第二天。

然后,如果您的表中的日期位于WeekendsHoliday表中,则您将结果连接到结果以获取“下一天”

DECLARE @WeekendsHoliday TABLE ([Weekends & Holidays] DATETIME)
INSERT INTO @WeekendsHoliday VALUES
('2016-03-05'),
('2016-03-06'),
('2016-03-07'),
('2016-03-12'),
('2016-03-13');

DECLARE @Schedule TABLE ([WorkDay] DATETIME)
INSERT INTO @Schedule VALUES
('2016-03-02'),
('2016-03-03'),
('2016-03-05'),
('2016-03-07'),
('2016-03-08'),
('2016-03-11'),
('2016-03-12');     

WITH RecursiveCTE AS
(
    SELECT
        [Weekends & Holidays],
        DATEADD(d, 1, [Weekends & Holidays]) AS [Next Day]
    FROM
        @WeekendsHoliday
    UNION ALL   
    SELECT 
        cte.[Weekends & Holidays],
        DATEADD(d, 1, [Next Day])
    FROM 
        RecursiveCTE cte
    WHERE 
        [Next Day] IN (SELECT [Weekends & Holidays] FROM @WeekendsHoliday)
),
AggregateCTE AS (
    SELECT 
        [Weekends & Holidays], 
        MAX([Next Day]) [Next Day] 
    FROM 
        RecursiveCTE 
    GROUP BY 
        [Weekends & Holidays]
)
SELECT  
    s.WorkDay,
    COALESCE(cte.[Next Day], s.WorkDay) AS [Adjusted Date Excluding holidays and weekends]
FROM 
    @Schedule s
    LEFT JOIN AggregateCTE cte ON s.[WorkDay] = cte.[Weekends & Holidays]

如果您只想查看已调整的日期,则使用INNER JOIN AggregateCTE代替LEFT JOIN AggregateCTE。如果可能的话,我还建议按开始日期和结束日期范围过滤WITH RecursiveCTE cte。

答案 1 :(得分:2)

<强> SQL Fiddle Demo

SELECT MIN(allDays.dte)
FROM (
      SELECT '2015-01-01' + INTERVAL ones.a + 10*tens.a + 100*hundred.a DAY dte
      FROM
       (SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 ) ones,
       (SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 ) tens,
       (SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 ) hundred
      WHERE '2015-01-01' + INTERVAL ones.a + 10*tens.a + 100*hundred.a DAY  <  '2016-01-01'
     ) allDays
LEFT JOIN holidays H
   ON allDays.dte = H.holydate
WHERE 
     H.holydate IS NULL
 AND allDays.dte >= '2015-12-12'  -- HERE go your Source DATE

<强>输出

| MIN(allDays.dte) |
|------------------|
|       2015-12-14 |  --Because 12 and 13 are holidays

<强> EXPLAIN

首先,您需要为allDays创建一个列表。在这里,我生成一个子查询来列出2015的所有日期,您需要适应大范围。

SELECT '2015-01-01' + INTERVAL ones.a + 10*tens.a + 100*hundred.a DAY dte
      FROM
       (SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 ) ones,
       (SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 ) tens,
       (SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 ) hundred
      WHERE '2015-01-01' + INTERVAL ones.a + 10*tens.a + 100*hundred.a DAY  <  '2016-01-01'

然后执行left join以查看DATE是否为假日

LEFT JOIN holidays H
   ON allDays.dte = H.holydate

最后选择不假期的较小日期

WHERE H.holydate IS NULL           -- NULL mean doesnt have a match in the holiday table
  AND allDays.dte >= '2015-12-12'  -- HERE go your Source DATE

答案 2 :(得分:2)

我建议创建一个函数,根据包含周末和假日的表递归验证下一个工作日。这种方法的优点是,无论何时需要,它都是可重用的功能。

此功能接收日期和时间。 (根据您问题中的代码)如果时间是在下午5点之后,则添加一天。之后,继续检查日期是否在周末或节假日之前,直到找到下一个工作日:

CREATE FUNCTION dbo.adjustedDate(@dateReceived DATETIME, @timeReceived TIME)
RETURNS DATETIME
AS
BEGIN
    DECLARE @adjustedDate DATETIME = @dateReceived

    -- Verify time to add 1 day to @adjustedDate
    IF @timeReceived IS NOT NULL
        IF @timeReceived > CONVERT(TIME, '5:00:00 PM')
             SET @adjustedDate = DATEADD(DAY, 1, @adjustedDate)

    -- Continue adding 1 day to @adjustedDate recursively until find one date that is not a weekend or holiday
    IF EXISTS(SELECT [Weekends & Holidays]
                FROM dbo.WeekendsHoliday
                WHERE [Weekends & Holidays] = @adjustedDate)                
        SET @adjustedDate = dbo.adjustedDate(DATEADD(DAY, 1, @adjustedDate), NULL)

    RETURN @adjustedDate
END

答案 3 :(得分:0)

我创建了非常简单的代码,在您的约会日期添加天数时将不包括假日和星期六星期日。首先,您必须为此创建假期表,然后您可以使用以下代码。我希望你能用我的代码得到你的结果,因为我创建了它并且它正常工作。

    ;WITH Numbers AS
(
    SELECT 1 AS value
    UNION ALL
    SELECT value + 1 AS value
    FROM Numbers
    WHERE Numbers.value <= 99
)
SELECT FinalTable.FromDate,FinalTable.AddedDays,FinalTable.AdjustedDate 
FROM (
    SELECT Final.*,ROW_Number() OVER (ORDER BY (SELECT NULL)) AS AddedDays 
    FROM (
        SELECT tbl.FromDate,CASE WHEN DATENAME(dw,tbl.AdjustedDate) = 'Saturday' THEN 0 
                                 WHEN DATENAME(dw,tbl.AdjustedDate) = 'Sunday' THEN 0
                                 WHEN tbl.AdjustedDate in (SELECT Holiday_Date FROM Holiday) THEN 0
                            ELSE 1 END AS LogicNumber ,tbl.days, tbl.AdjustedDate 
        FROM (
            SELECT @FromDate AS FromDate, DATEADD(DAY,num,@FromDate) AS AdjustedDate, num AS days 
            FROM ( 
                  SELECT ROW_Number() OVER (ORDER BY (SELECT NULL)) AS num  FROM Numbers
                 ) t
            WHERE num <= 100
             ) tbl 
        )Final 
        WHERE LogicNumber = 1 
    )FinalTable 
    WHERE AddedDays = @days

让我们说@FromDate =&#39; 2017-12-30&#39; 2018年1月1日是假期表中的假日,我们必须增加10天,即@days = 10

输出

FromDate    | AddedDays | AdjustedDate
2017-12-30  | 10        | 2018-01-15