TSQL - 添加工作日,不包括假期

时间:2017-01-26 20:20:19

标签: sql-server tsql stored-procedures sql-server-2012 user-defined-functions

我正在处理一种情况,即我有一个与特定工作日相关的优先级列表。

例如,如果我今天的日期为2017-01-26,并且需要为其添加5个工作日,则会返回2017-02-02

为了混合一些附加功能,我有一个需要排除的公司定义假期表。如果我从5选择2017-02-16个工作日,则会看到美国总统日落在2017-02-20上并且会跳过此日期,从而5th营业日2017-02-24

我找到了另一个关于此问题的例子,我正在努力适应我的需要。问题是,我并不确切知道发生了什么,只是因为我的添加不会导致我的期望。

参考文章:https://stackoverflow.com/a/12862675/2628921

ALTER FUNCTION [dbo].[findBusinessDayAfter]
(@date DATETIME, @days INT)
RETURNS DATE
AS
BEGIN
    RETURN (SELECT thedate
            FROM   (SELECT dateadd(d, v.day, CAST (@date AS DATE)) AS thedate,
                           row_number() OVER ( ORDER BY v.day) AS rn
                    FROM   (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), (31), (32), (33), (34), (35), (36), (37), (38), (39), (40)) AS v(day)
                           LEFT OUTER JOIN
                           holidays AS h
                           ON h.holidayDate = dateadd(d, v.day, CAST (@date AS DATE))
                    WHERE  h.holidayDate IS NULL
                           AND LEFT(datename(dw, dateadd(d, v.day, CAST (@date AS DATE))), 1) <> 'S') AS x
            WHERE  @days = rn);
END

在给出的原始示例中,他们使用(1) - (10)。但是,我的情况是我需要将35-40个工作日添加到某个日期,因此我相应地调整了查询​​以允许我进行更新。

我发现当我尝试在某个工作日运行某些日期时,它会返回null值并且我不确定原因。

-- Multiple Holidays (Day before Thanksgiving)
SELECT dbo.findBusinessDayAfter('2017-11-05', 35) -- Returns NULL

我不确定原始代码段中发生的情况会阻止我将其默认10 days增加到30+天。

有关如何调整以适应我的用例的任何想法?

1 个答案:

答案 0 :(得分:0)

我尝试通过这样做来运行SP的内部:

DECLARE @holidays TABLE(holidayDate DATE);
INSERT INTO @holidays
       SELECT *
       FROM(VALUES('20170103')) f(b);
DECLARE @date DATETIME= '20170101', @days INT= 1;

然后我运行内部选择语句

SELECT DATEADD(d, v.day, CAST(@date AS DATE)) AS thedate,
       ROW_NUMBER() OVER(ORDER BY v.day) AS rn
FROM(VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), (31), (32), (33), (34), (35), (36), (37), (38), (39), (40)) AS v(day)
    LEFT OUTER JOIN @holidays AS h ON h.holidayDate = DATEADD(d, v.day, CAST(@date AS DATE))
WHERE h.holidayDate IS NULL
      AND LEFT(DATENAME(dw, DATEADD(d, v.day, CAST(@date AS DATE))), 1) <> 'S'

这会列出工作日的天数,从提供的日期开始,到最接近日期+ 45天的日历日期的工作日结束。 所有假期和周末都将从列表中删除。根据我输入的值,从2017年1月1日到2月15日,即1月1日后的45个日历日,有29个工作日。 所以这是你的问题,你想要工作35个工作日,但在接下来的45个日历日里,工作日不会那么多。

我希望这能说清楚代码中发生了什么,以及它失败的原因。 现在快速解决的问题是期待更多日历日。 在这里,我与你的45天交叉加入3次,并计算从1到45 * 45 * 45-1天的跑步总数

    SELECT DATEADD(d, v1.day+45*(v2.day-1)+45*45*(v3.day-1), CAST(@date AS DATE)) AS thedate,
           ROW_NUMBER() OVER(ORDER BY v1.day+45*(v2.day-1)+45*45*(v3.day-1)) AS rn
    FROM
       (VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), (31), (32), (33), (34), (35), (36), (37), (38), (39), (40)) AS v1(day)
cross join     
       (VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), (31), (32), (33), (34), (35), (36), (37), (38), (39), (40)) AS v2(day)
cross join     
       (VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), (31), (32), (33), (34), (35), (36), (37), (38), (39), (40)) AS v3(day)
        LEFT OUTER JOIN @holidays AS h ON h.holidayDate = DATEADD(d, v1.day+45*(v2.day-1)+45*45*(v3.day-1), CAST(@date AS DATE))
    WHERE h.holidayDate IS NULL
          AND LEFT(DATENAME(dw, DATEADD(d, v1.day+45*(v2.day-1)+45*45*(v3.day-1), CAST(@date AS DATE))), 1) <> 'S'

这使您有可能提前数千天前往。它可能不是最优雅的解决方案,但它应该可以解决您的问题。