0

我正在处理的情况是,我有一个优先级列表与特定数量的工作日相关联。TSQL - 增加营业日至日期,不包括节假日

例如,如果我今天的日期为2017-01-26,需要增加5个工作日,则它将返回2017-02-02

要混合一些附加功能,我有一张公司定义的假期表,它需要排除。如果我从2017-02-16中选择5工作日,则会看到美国总统日落在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 days30+天发生。

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

+2

为什么不创建日历表并做这样的事情? HTTPS://www.mssqltips。COM/sqlservertip/4054 /创建-A-日期维度或日历表,在-SQL服务器/ – dadde

回答

0

我试图这样做,运行你的SP内部:

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

我再运行内部select语句

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' 

这给你提前去几千天的可能性计算运行总和。它可能不是最优雅的解决方案,但它应该可以解决您的问题。