Add business days to date in SQL without loops

后端 未结 25 2754
无人共我
无人共我 2020-12-02 22:54

I currently have a function in my SQL database that adds a certain amount of business days to a date, e.g. if you enter a date that is a Thursday and add two days, it will r

相关标签:
25条回答
  • 2020-12-02 23:38

    I've very recently solved this problem to add two working days to the current date by creating an INT value @DaysToAdd - tested and working great on 2008 / 2012.

    DECLARE @DaysToAdd INT
    
    SELECT @DaysToAdd = CASE  
       WHEN DATEPART(WEEKDAY,GETDATE()) =  1 THEN 3 -- Sunday -> Wednesday
       WHEN DATEPART(WEEKDAY,GETDATE()) =  5 THEN 4 -- Thursday -> Monday
       WHEN DATEPART(WEEKDAY,GETDATE()) =  6 THEN 4 -- Friday -> Tuesday
       WHEN DATEPART(WEEKDAY,GETDATE()) =  7 THEN 4 -- Saturday -> Wednesday
       ELSE 2 END
    
    SELECT DATEADD(DAY, @DaysToAdd, GETDATE()) AS TwoWorkingDaysTime
    
    0 讨论(0)
  • 2020-12-02 23:42

    I found a much more elegant approach from Microsoft Docs. It takes into account skipping multiple weekends. Super clean.

    CREATE FUNCTION DAYSADDNOWK(@addDate AS DATE, @numDays AS INT)
    RETURNS DATETIME
    AS
    BEGIN
        WHILE @numDays>0
        BEGIN
           SET @addDate=DATEADD(d,1,@addDate)
           IF DATENAME(DW,@addDate)='saturday' SET @addDate=DATEADD(d,1,@addDate)
           IF DATENAME(DW,@addDate)='sunday' SET @addDate=DATEADD(d,1,@addDate)
    
           SET @numDays=@numDays-1
        END
    
        RETURN CAST(@addDate AS DATETIME)
    END
    GO
    

    Run the test

    SELECT dbo.DAYSADDNOWK(GETDATE(), 15)
    
    0 讨论(0)
  • 2020-12-02 23:43

    This is better if anyone is looking for a TSQL solution. No loops, no tables, no case statements AND works with negatives. Can anyone beat that?

    CREATE FUNCTION[dbo].[AddBusinessDays](@Date date,@n INT)
    RETURNS DATE AS 
    BEGIN
    DECLARE @d INT;SET @d=4-SIGN(@n)*(4-DATEPART(DW,@Date));
    RETURN DATEADD(D,@n+((ABS(@n)+@d-2)/5)*2*SIGN(@n)-@d/7,@Date);
    END
    
    0 讨论(0)
  • 2020-12-02 23:43

    Building off of the answer that was accepted for this question, the following user-defined function (UDF) should work in all cases--regardless of the setting for @@DateFirst.

    UPDATE: As comments below indicate, this function is designed for the FromDate to be a weekday. The behavior is undefined when a weekend day is passed in as the FromDate.

    ALTER FUNCTION [dbo].[BusinessDaysDateAdd] 
    (
       @FromDate datetime,
       @DaysToAdd int
    )
    RETURNS datetime
    AS
    BEGIN
       DECLARE @Result datetime
    
       SET @Result = DATEADD(day, (@DaysToAdd % 5) + CASE ((@@DATEFIRST + DATEPART(weekday, @FromDate) + (@DaysToAdd % 5)) % 7)
                                                     WHEN 0 THEN 2
                                                     WHEN 1 THEN 1
                                                     ELSE 0 END, DATEADD(week, (@DaysToAdd / 5), @FromDate))
    
       RETURN @Result
    END
    
    0 讨论(0)
  • 2020-12-02 23:43

    This is an old thread but I just created a table with all the dates then did this:

    SELECT Count(*) 
    FROM Date_Table
    WHERE [day] BETWEEN @StartDate and @EndDate
        AND DATENAME(weekday, [day]) NOT IN ('Sunday', 'Saturday')
    
    0 讨论(0)
  • 2020-12-02 23:43

    I know it's a little bit late, perhaps someone else stumble upon this problem. I've tried the above solution but, most of them can't calculate holidays.

    This is how i tried

    CREATE function [dbo].[DateAddWorkDay]
    (@days int,@FromDate Date)
    returns Date
    as
    begin
    declare @result date
    set @result = (
    select b
    from
    (
        SELECT
        b,
           (DATEDIFF(dd, a, b))
          -(DATEDIFF(wk, a, b) * 2)
          -(CASE WHEN DATENAME(dw, a) = 'Sunday' THEN 1 ELSE 0 END)
          -(CASE WHEN DATENAME(dw, b) = 'Saturday' THEN 1 ELSE 0 END)
          -COUNT(o.Holiday_Date) 
          as workday
        from
        (
        select 
        @FromDate as a,
        dateadd(DAY,num +@days,@FromDate) as b
        from (select row_number() over (order by (select NULL)) as num
              from Information_Schema.columns
             ) t
        where num <= 100 
        ) dt
        left join Holiday o on o.Holiday_Date between a and b and DATENAME(dw, o.Holiday_Date) not in('Saturday','Sunday') 
        where DATENAME(dw, b) not in('Saturday','Sunday')
              and b not in (select Holiday_Date from OP_Holiday where Holiday_Date between a and b) 
    
        group by a,b
    ) du
    where workday =@days 
    
    
    )
    return @result 
    end
    

    Where Holiday is a table with holiday_date as a reference for holiday

    Hope this can help some one.

    0 讨论(0)
提交回复
热议问题