Count work days between two dates

前端 未结 24 2704
失恋的感觉
失恋的感觉 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:35

    All Credit to Bogdan Maxim & Peter Mortensen. This is their post, I just added holidays to the function (This assumes you have a table "tblHolidays" with a datetime field "HolDate".

    --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)
            --Subtract all holidays
            -(Select Count(*) from [DB04\DB04].[Gateway].[dbo].[tblHolidays]
              where  [HolDate] between @StartDate and @EndDate )
            )
        END  
    GO
    -- Test Script
    /*
    declare @EndDate datetime= dateadd(m,2,getdate())
    print @EndDate
    select  [Master].[dbo].[fn_WorkDays] (getdate(), @EndDate)
    */
    
    0 讨论(0)
  • 2020-11-22 01:36

    (I'm a few points shy of commenting privileges)

    If you decide to forgo the +1 day in CMS's elegant solution, note that if your start date and end date are in the same weekend, you get a negative answer. Ie., 2008/10/26 to 2008/10/26 returns -1.

    my rather simplistic solution:

    select @Result = (..CMS's answer..)
    if  (@Result < 0)
            select @Result = 0
        RETURN @Result
    

    .. which also sets all erroneous posts with start date after end date to zero. Something you may or may not be looking for.

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

    For difference between dates including holidays I went this way:

    1) Table with Holidays:

        CREATE TABLE [dbo].[Holiday](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL,
    [Date] [datetime] NOT NULL)
    

    2) I had my plannings Table like this and wanted to fill column Work_Days which was empty:

        CREATE TABLE [dbo].[Plan_Phase](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Id_Plan] [int] NOT NULL,
    [Id_Phase] [int] NOT NULL,
    [Start_Date] [datetime] NULL,
    [End_Date] [datetime] NULL,
    [Work_Days] [int] NULL)
    

    3) So in order to get "Work_Days" to later fill in my column just had to:

    SELECT Start_Date, End_Date,
     (DATEDIFF(dd, Start_Date, End_Date) + 1)
    -(DATEDIFF(wk, Start_Date, End_Date) * 2)
    -(SELECT COUNT(*) From Holiday Where Date  >= Start_Date AND Date <= End_Date)
    -(CASE WHEN DATENAME(dw, Start_Date) = 'Sunday' THEN 1 ELSE 0 END)
    -(CASE WHEN DATENAME(dw, End_Date) = 'Saturday' THEN 1 ELSE 0 END)
    -(CASE WHEN (SELECT COUNT(*) From Holiday Where Start_Date  = Date) > 0 THEN 1 ELSE 0 END)
    -(CASE WHEN (SELECT COUNT(*) From Holiday Where End_Date  = Date) > 0 THEN 1 ELSE 0 END) AS Work_Days
    from Plan_Phase
    

    Hope that I could help.

    Cheers

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

    For workdays, Monday to Friday, you can do it with a single SELECT, like this:

    DECLARE @StartDate DATETIME
    DECLARE @EndDate DATETIME
    SET @StartDate = '2008/10/01'
    SET @EndDate = '2008/10/31'
    
    
    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)
    

    If you want to include holidays, you have to work it out a bit...

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

    My version of the accepted answer as a function using DATEPART, so I don't have to do a string comparison on the line with

    DATENAME(dw, @StartDate) = 'Sunday'
    

    Anyway, here's my business datediff function

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE FUNCTION BDATEDIFF
    (
        @startdate as DATETIME,
        @enddate as DATETIME
    )
    RETURNS INT
    AS
    BEGIN
        DECLARE @res int
    
    SET @res = (DATEDIFF(dd, @startdate, @enddate) + 1)
        -(DATEDIFF(wk, @startdate, @enddate) * 2)
        -(CASE WHEN DATEPART(dw, @startdate) = 1 THEN 1 ELSE 0 END)
        -(CASE WHEN DATEPART(dw, @enddate) = 7 THEN 1 ELSE 0 END)
    
        RETURN @res
    END
    GO
    
    0 讨论(0)
  • 2020-11-22 01:39

    I borrowed some ideas from others to create my solution. I use inline code to ignore weekends and U.S. federal holidays. In my environment, EndDate may be null, but it will never precede StartDate.

    CREATE FUNCTION dbo.ufn_CalculateBusinessDays(
    @StartDate DATE,
    @EndDate DATE = NULL)
    
    RETURNS INT
    AS
    
    BEGIN
    DECLARE @TotalBusinessDays INT = 0;
    DECLARE @TestDate DATE = @StartDate;
    
    
    IF @EndDate IS NULL
        RETURN NULL;
    
    WHILE @TestDate < @EndDate
    BEGIN
        DECLARE @Month INT = DATEPART(MM, @TestDate);
        DECLARE @Day INT = DATEPART(DD, @TestDate);
        DECLARE @DayOfWeek INT = DATEPART(WEEKDAY, @TestDate) - 1; --Monday = 1, Tuesday = 2, etc.
        DECLARE @DayOccurrence INT = (@Day - 1) / 7 + 1; --Nth day of month (3rd Monday, for example)
    
        --Increment business day counter if not a weekend or holiday
        SELECT @TotalBusinessDays += (
            SELECT CASE
                --Saturday OR Sunday
                WHEN @DayOfWeek IN (6,7) THEN 0
                --New Year's Day
                WHEN @Month = 1 AND @Day = 1 THEN 0
                --MLK Jr. Day
                WHEN @Month = 1 AND @DayOfWeek = 1 AND @DayOccurrence = 3 THEN 0
                --G. Washington's Birthday
                WHEN @Month = 2 AND @DayOfWeek = 1 AND @DayOccurrence = 3 THEN 0
                --Memorial Day
                WHEN @Month = 5 AND @DayOfWeek = 1 AND @Day BETWEEN 25 AND 31 THEN 0
                --Independence Day
                WHEN @Month = 7 AND @Day = 4 THEN 0
                --Labor Day
                WHEN @Month = 9 AND @DayOfWeek = 1 AND @DayOccurrence = 1 THEN 0
                --Columbus Day
                WHEN @Month = 10 AND @DayOfWeek = 1 AND @DayOccurrence = 2 THEN 0
                --Veterans Day
                WHEN @Month = 11 AND @Day = 11 THEN 0
                --Thanksgiving
                WHEN @Month = 11 AND @DayOfWeek = 4 AND @DayOccurrence = 4 THEN 0
                --Christmas
                WHEN @Month = 12 AND @Day = 25 THEN 0
                ELSE 1
                END AS Result);
    
        SET @TestDate = DATEADD(dd, 1, @TestDate);
    END
    
    RETURN @TotalBusinessDays;
    END
    
    0 讨论(0)
提交回复
热议问题