TSQL - 添加迄今为止的工作日,不包括假期

Posted

技术标签:

【中文标题】TSQL - 添加迄今为止的工作日,不包括假期【英文标题】:TSQL - Add business days to date, not including holidays 【发布时间】:2017-01-26 20:20:19 【问题描述】:

我正在处理与特定工作日数相关联的优先级列表的情况。

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

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

我在 SO 上找到了另一个示例,我正在尝试适应我的需求。问题是,我不完全知道发生了什么,只是我的添加没有达到我的预期。

参考帖:https://***.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

我不确定原始代码 sn-p 中发生了什么会阻止我将默认 10 days 增加到我的 30+ 天数。

有什么想法可以适应我的用例吗?

【问题讨论】:

为什么不创建一个日历表并做这样的事情呢? mssqltips.com/sqlservertip/4054/… 【参考方案1】:

我尝试通过这样做来运行您的 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'

这列出了工作日,从提供的日期开始,到最接近@date+45 天日历日期的工作日结束。 所有假期和周末都将从列表中删除。使用我输入的值,从 2017 年 1 月 1 日到 2 月 15 日有 29 个工作日,即 1 月 1 日之后的 45 个日历日。 所以这是您的问题,您想要 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'

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

【讨论】:

以上是关于TSQL - 添加迄今为止的工作日,不包括假期的主要内容,如果未能解决你的问题,请参考以下文章

在Python中动态计算不包括假期日历的工作日数

如何在 SQL 或 PHP 中获取不包括假期(法语)和工作日的最后三天

在 TSQL 中,如何添加一个计数列来计算查询中的行数?

计算包括节假日在内的工作日

C#如何计算不包括特定日期(节假日)的工作日

TSQL - 每组删除除 1 之外的所有行