【问题标题】:SQL - Subtract Business Days from DateSQL - 从日期中减去工作日
【发布时间】:2019-09-06 16:50:11
【问题描述】:

我正在尝试在我的表中查询日期字段介于 10 个工作日前和今天之间的记录。我正在努力计算 10 个工作日前的日期。

就我而言,假期无关紧要。无论假期/工作日如何,我只需要包括周一 - 周五。

我找到了这个 SQL 代码,它可以满足我的要求,但是是倒退的。这计算了两个日期之间的工作日数,我需要减去一个数字并得到一个日期。

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SET @StartDate = '2019-08-26'
SET @EndDate = GETDATE()

SELECT
   (DATEDIFF(dd, @StartDate, @EndDate) + 1)
  -(DATEDIFF(wk, @StartDate, @EndDate) * 2)
  -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN 1 ELSE 0 END)
  -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)

我尝试过使用DATEADD,但无法找出排除周六和周日的任何逻辑。我也不想使用一个函数,我不是绝对必须的。如果没有每天循环播放的功能,我无法在线找到有关 DATEADD 的任何信息。

所需功能:

START DATE: TODAY / GETDATE()
NUMBER OF BUSINESS DAYS: 10

DATEADD(DAY, -10 + CALULATE WEEKEND DAYS?, GETDATE())

【问题讨论】:

  • 可能最简单、最灵活、最模块化、最易维护的方法就是生成一个日期表,简单地统计开始日期和结束日期之间的行数,不包括周末天数(例如@ 987654324@)。以数学方式和内联方式进行比较可能会非常复杂,并且难以验证所有特殊情况。 (编辑:重做评论以修复格式。)
  • 如果只关心周末而忽略节假日,可以使用如下公式:DATEADD(DAY, -7*(@bdays / 5) + @bdays % 5, @start_date)。然后如果结果落在星期六减去 1 天,如果它落在星期日减去 2 天。
  • 上述公式有误,但无法编辑评论。正确的是:DATEADD(DAY, -7*(@bdays / 5) - @bdays % 5, @start_date).
  • @P.Kouvarakis 我不得不稍微修改一下以使其包含开始日期(我忘了提及 - 抱歉),但在写了一个案例语句以捕获它之后它工作了周六或周日结束。谢谢。如果您想将其作为完整答案输入,我很乐意接受。
  • 我将支持史蒂夫的上述建议。在诸如此类的多种情况下,日期表可以方便且简单/干净。

标签: sql sql-server dateadd


【解决方案1】:

由于您只关心周末而不关心其他节假日,因此使用每个日历周(7 天)有 5 个工作日的知识,很容易从工作日计算日历天数。如果您可以使用标量函数,则可以使用以下内容:

CREATE FUNCTION SubBusinessDays(
    @days int,
    @date datetime
) RETURNS int
BEGIN

    SET @days = @days-1; -- number of days are inclusive of the start date

    SET @date = DATEADD(DAY, -(@days / 5 * 7 + @days % 5),
                    DATEADD(DAY, (CASE (DATEPART(WEEKDAY, @date) + @@DATEFIRST) % 7 
                                      WHEN 0 THEN -1 
                                      WHEN 1 THEN -2 
                                      ELSE 0 END), @date));
    RETURN DATEADD(DAY, (CASE WHEN (DATEPART(WEEKDAY, @date) + @@DATEFIRST) % 7 IN (0, 1) 
                              THEN -2 ELSE 0 END), @date);
END

另一方面,如果您需要处理一张桌子,您可以使用CROSS APPLY 按照以下示例中的步骤执行相同的操作:

DECLARE @t TABLE(StartDate Datetime, BDays int)

INSERT INTO @t
SELECT d, ofs
FROM (VALUES ('20190906'), ('20190907'), ('20190908'), ('20190909'), ('20190910')) AS sd(d)
    CROSS JOIN 
     (VALUES (7), (8), (9), (10), (11)) AS bd(ofs)


SELECT StartDate, BDays, EndDate
FROM @t
    CROSS APPLY (SELECT BDaysMinus1 = BDays-1) x1
    CROSS APPLY (SELECT EndDateTemp = DATEADD(DAY, -((BDaysMinus1 / 5) * 7 + BDaysMinus1 % 5),
                    DATEADD(DAY, (CASE (DATEPART(WEEKDAY, StartDate) + @@DATEFIRST) % 7  
                                        WHEN 0 THEN -1 
                                        WHEN 1 THEN -2 
                                        ELSE 0 END), StartDate))) x2
    CROSS APPLY (SELECT EndDate = DATEADD(DAY, (CASE WHEN (DATEPART(WEEKDAY, EndDateTemp) + @@DATEFIRST) % 7 IN (0,1) 
                                        THEN -2 ELSE 0 END), EndDateTemp)) x3

【讨论】:

    【解决方案2】:

    试试这个递归 CTE,如下所示-

    DECLARE @start_date DATE= GETDATE(), 
        @NumberOfDays INT= 10;
    
    
    WITH cte
    AS 
    (               
        SELECT @start_date AS date_
    
        UNION ALL
    
        SELECT CAST(DATEADD(day, -1, date_) AS DATE)
        FROM cte
        WHERE date_ >= DATEADD(
            DD,
            -(@NumberOfDays   + (@NumberOfDays/7+1)*3), 
            --Generating a approximate number consediring  
            --Number Of Business Day + Approximate Week Ends for that days
            @start_date
        )
    ),
    CTE2 AS
    (
        SELECT Date_,ROW_NUMBER() OVER (ORDER BY Date_ DESC) RN
        FROM cte 
        WHERE DATENAME(dw, Date_) NOT IN ('Saturday','Sunday')
    )
    
    SELECT Date_ 
    FROM CTE2
    WHERE  RN = @NumberOfDays
    OPTION(MAXRECURSION 0)
    

    【讨论】:

    • 似乎很适合增加工作日。我需要它来减去工作日。可以将其修改为从 GETDATE() 开始向后退吗?
    • 您可以设置任何日期来代替 GETDATE()
    • 我明白这一点。但我需要从GETDATE()(今天)开始,然后倒数到任何数字(@NumberOfDays)工作日。您的解决方案适用于从 GETDATE() 开始并向前计算任何工作日数。
    • 哦,我明白了。我已经更新了代码,它应该可以工作。当我使用手机更新代码时,我无法检查语法,但相应地设置了逻辑。我可以稍后检查是否存在任何语法问题。但我希望现在可以了。 @dvo
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-26
    • 2018-04-11
    相关资源
    最近更新 更多