Count work days between two dates

前端 未结 24 2707
失恋的感觉
失恋的感觉 2020-11-22 01:05

How can I calculate the number of work days between two dates in SQL Server?

Monday to Friday and it must be T-SQL.

相关标签:
24条回答
  • 2020-11-22 01:41

    Here is a version that works well (I think). Holiday table contains Holiday_date columns that contains holidays your company observe.

    DECLARE @RAWDAYS INT
    
       SELECT @RAWDAYS =  DATEDIFF(day, @StartDate, @EndDate )--+1
                        -( 2 * DATEDIFF( week, @StartDate, @EndDate ) )
                        + CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END
                        - CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END 
    
       SELECT  @RAWDAYS - COUNT(*) 
         FROM HOLIDAY NumberOfBusinessDays
        WHERE [Holiday_Date] BETWEEN @StartDate+1 AND @EndDate 
    
    0 讨论(0)
  • 2020-11-22 01:42

    In Calculating Work Days you can find a good article about this subject, but as you can see it is not that advanced.

    --Changing current database to the Master database allows function to be shared by everyone.
    USE MASTER
    GO
    --If the function already exists, drop it.
    IF EXISTS
    (
        SELECT *
        FROM dbo.SYSOBJECTS
        WHERE ID = OBJECT_ID(N'[dbo].[fn_WorkDays]')
        AND XType IN (N'FN', N'IF', N'TF')
    )
    DROP FUNCTION [dbo].[fn_WorkDays]
    GO
     CREATE FUNCTION dbo.fn_WorkDays
    --Presets
    --Define the input parameters (OK if reversed by mistake).
    (
        @StartDate DATETIME,
        @EndDate   DATETIME = NULL --@EndDate replaced by @StartDate when DEFAULTed
    )
    
    --Define the output data type.
    RETURNS INT
    
    AS
    --Calculate the RETURN of the function.
    BEGIN
        --Declare local variables
        --Temporarily holds @EndDate during date reversal.
        DECLARE @Swap DATETIME
    
        --If the Start Date is null, return a NULL and exit.
        IF @StartDate IS NULL
            RETURN NULL
    
        --If the End Date is null, populate with Start Date value so will have two dates (required by DATEDIFF below).
         IF @EndDate IS NULL
            SELECT @EndDate = @StartDate
    
        --Strip the time element from both dates (just to be safe) by converting to whole days and back to a date.
        --Usually faster than CONVERT.
        --0 is a date (01/01/1900 00:00:00.000)
         SELECT @StartDate = DATEADD(dd,DATEDIFF(dd,0,@StartDate), 0),
                @EndDate   = DATEADD(dd,DATEDIFF(dd,0,@EndDate)  , 0)
    
        --If the inputs are in the wrong order, reverse them.
         IF @StartDate > @EndDate
            SELECT @Swap      = @EndDate,
                   @EndDate   = @StartDate,
                   @StartDate = @Swap
    
        --Calculate and return the number of workdays using the input parameters.
        --This is the meat of the function.
        --This is really just one formula with a couple of parts that are listed on separate lines for documentation purposes.
         RETURN (
            SELECT
            --Start with total number of days including weekends
            (DATEDIFF(dd,@StartDate, @EndDate)+1)
            --Subtact 2 days for each full weekend
            -(DATEDIFF(wk,@StartDate, @EndDate)*2)
            --If StartDate is a Sunday, Subtract 1
            -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday'
                THEN 1
                ELSE 0
            END)
            --If EndDate is a Saturday, Subtract 1
            -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday'
                THEN 1
                ELSE 0
            END)
            )
        END
    GO
    

    If you need to use a custom calendar, you might need to add some checks and some parameters. Hopefully it will provide a good starting point.

    0 讨论(0)
  • 2020-11-22 01:44
    Create Function dbo.DateDiff_WeekDays 
    (
    @StartDate  DateTime,
    @EndDate    DateTime
    )
    Returns Int
    As
    
    Begin   
    
    Declare @Result Int = 0
    
    While   @StartDate <= @EndDate
    Begin 
        If DateName(DW, @StartDate) not in ('Saturday','Sunday')
            Begin
                Set @Result = @Result +1
            End
            Set @StartDate = DateAdd(Day, +1, @StartDate)
    End
    
    Return @Result
    

    End

    0 讨论(0)
  • 2020-11-22 01:45

    This is basically CMS's answer without the reliance on a particular language setting. And since we're shooting for generic, that means it should work for all @@datefirst settings as well.

    datediff(day, <start>, <end>) + 1 - datediff(week, <start>, <end>) * 2
        /* if start is a Sunday, adjust by -1 */
      + case when datepart(weekday, <start>) = 8 - @@datefirst then -1 else 0 end
        /* if end is a Saturday, adjust by -1 */
      + case when datepart(weekday, <end>) = (13 - @@datefirst) % 7 + 1 then -1 else 0 end
    

    datediff(week, ...) always uses a Saturday-to-Sunday boundary for weeks, so that expression is deterministic and doesn't need to be modified (as long as our definition of weekdays is consistently Monday through Friday.) Day numbering does vary according to the @@datefirst setting and the modified calculations handle this correction with the small complication of some modular arithmetic.

    A cleaner way to deal with the Saturday/Sunday thing is to translate the dates prior to extracting a day of week value. After shifting, the values will be back in line with a fixed (and probably more familiar) numbering that starts with 1 on Sunday and ends with 7 on Saturday.

    datediff(day, <start>, <end>) + 1 - datediff(week, <start>, <end>) * 2
      + case when datepart(weekday, dateadd(day, @@datefirst, <start>)) = 1 then -1 else 0 end
      + case when datepart(weekday, dateadd(day, @@datefirst, <end>))   = 7 then -1 else 0 end
    

    I've tracked this form of the solution back at least as far as 2002 and an Itzik Ben-Gan article. (https://technet.microsoft.com/en-us/library/aa175781(v=sql.80).aspx) Though it needed a small tweak since newer date types don't allow date arithmetic, it is otherwise identical.

    EDIT: I added back the +1 that had somehow been left off. It's also worth noting that this method always counts the start and end days. It also assumes that the end date is on or after the start date.

    0 讨论(0)
  • 2020-11-22 01:49
    DECLARE @StartDate datetime,@EndDate datetime
    
    select @StartDate='3/2/2010', @EndDate='3/7/2010'
    
    DECLARE @TotalDays INT,@WorkDays INT
    
    DECLARE @ReducedDayswithEndDate INT
    
    DECLARE @WeekPart INT
    
    DECLARE @DatePart INT
    
    SET @TotalDays= DATEDIFF(day, @StartDate, @EndDate) +1
    
    SELECT @ReducedDayswithEndDate = CASE DATENAME(weekday, @EndDate)
        WHEN 'Saturday' THEN 1
        WHEN 'Sunday' THEN 2
        ELSE 0 END
    
    SET @TotalDays=@TotalDays-@ReducedDayswithEndDate
    
    SET @WeekPart=@TotalDays/7;
    
    SET @DatePart=@TotalDays%7;
    
    SET @WorkDays=(@WeekPart*5)+@DatePart
    
    SELECT @WorkDays
    
    0 讨论(0)
  • 2020-11-22 01:49

    I took the various examples here, but in my particular situation we have a @PromisedDate for delivery and a @ReceivedDate for the actual receipt of the item. When an item was received before the "PromisedDate" the calculations were not totaling correctly unless I ordered the dates passed into the function by calendar order. Not wanting to check the dates every time, I changed the function to handle this for me.

    Create FUNCTION [dbo].[fnGetBusinessDays]
    (
     @PromiseDate date,
     @ReceivedDate date
    )
    RETURNS integer
    AS
    BEGIN
     DECLARE @days integer
    
     SELECT @days = 
        Case when @PromiseDate > @ReceivedDate Then
            DATEDIFF(d,@PromiseDate,@ReceivedDate) + 
            ABS(DATEDIFF(wk,@PromiseDate,@ReceivedDate)) * 2 +
            CASE 
                WHEN DATENAME(dw, @PromiseDate) <> 'Saturday' AND DATENAME(dw, @ReceivedDate) = 'Saturday' THEN 1 
                WHEN DATENAME(dw, @PromiseDate) = 'Saturday' AND DATENAME(dw, @ReceivedDate) <> 'Saturday' THEN -1 
                ELSE 0
            END +
            (Select COUNT(*) FROM CompanyHolidays 
                WHERE HolidayDate BETWEEN @ReceivedDate AND @PromiseDate 
                AND DATENAME(dw, HolidayDate) <> 'Saturday' AND DATENAME(dw, HolidayDate) <> 'Sunday')
        Else
            DATEDIFF(d,@PromiseDate,@ReceivedDate)  -
            ABS(DATEDIFF(wk,@PromiseDate,@ReceivedDate)) * 2  -
                CASE 
                    WHEN DATENAME(dw, @PromiseDate) <> 'Saturday' AND DATENAME(dw, @ReceivedDate) = 'Saturday' THEN 1 
                    WHEN DATENAME(dw, @PromiseDate) = 'Saturday' AND DATENAME(dw, @ReceivedDate) <> 'Saturday' THEN -1 
                    ELSE 0
                END -
            (Select COUNT(*) FROM CompanyHolidays 
                WHERE HolidayDate BETWEEN @PromiseDate and @ReceivedDate 
                AND DATENAME(dw, HolidayDate) <> 'Saturday' AND DATENAME(dw, HolidayDate) <> 'Sunday')
        End
    
    
     RETURN (@days)
    
    END
    
    0 讨论(0)
提交回复
热议问题