Đếm số ngày làm việc giữa hai ngày


162

Làm cách nào để tính số ngày làm việc giữa hai ngày trong SQL Server?

Thứ Hai đến thứ Sáu và nó phải là T-SQL.


5
Bạn có thể định nghĩa ngày làm việc? Thứ Hai nào đến thứ Sáu? Không bao gồm các ngày lễ lớn? Những gì đất nước? Nó phải được thực hiện trong SQL?
Dave K

Câu trả lời:


300

Đối với ngày làm việc, từ thứ Hai đến thứ Sáu, bạn có thể thực hiện với một CHỌN duy nhất, như thế này:

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)

Nếu bạn muốn bao gồm các ngày lễ, bạn phải giải quyết một chút ...


3
Tôi chỉ nhận ra rằng mã này không hoạt động luôn! tôi đã thử điều này: SET @StartDate = '28 -mar-2011 'SET @EndDate = '29 -mar-2011', câu trả lời được tính là 2 ngày
hy vọng vào

16
@galettreat Nó hoạt động tốt. Chỉ là cả @StartDate và @EndDate đều được tính vào. Nếu bạn muốn thứ Hai đến thứ Ba được tính là 1 ngày, chỉ cần xóa "+ 1" sau DATEDIFF đầu tiên. Sau đó, bạn cũng sẽ nhận được Fri-> Sat = 0, Fri-> Sun = 0, Fri-> Mon = 1.
Joe Daley

6
Theo dõi @JoeDaley. Khi bạn xóa +1 sau DATEDIFF để loại trừ ngày bắt đầu khỏi số đếm, bạn cũng cần điều chỉnh phần CASE của phần này. Tôi đã kết thúc bằng cách sử dụng cái này: + (CASE KHI DATENAME (dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE KHI DATENAME (dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Sequenzia

7
Hàm datename phụ thuộc vào miền địa phương. Một giải pháp mạnh mẽ hơn nhưng cũng khó hiểu hơn là thay thế hai dòng cuối bằng cách:-(case datepart(dw, @StartDate)+@@datefirst when 8 then 1 else 0 end) -(case datepart(dw, @EndDate)+@@datefirst when 7 then 1 when 14 then 1 else 0 end)
Torben Klein

2
Để làm rõ nhận xét của @ Sequenzia, bạn sẽ XÓA toàn bộ báo cáo trường hợp về Chủ nhật, chỉ để lại+(CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Andy Raddatz

32

Trong Tính toán ngày làm việc, bạn có thể tìm thấy một bài viết hay về chủ đề này, nhưng như bạn có thể thấy nó không phải là nâng cao.

--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

Nếu bạn cần sử dụng lịch tùy chỉnh, bạn có thể cần thêm một số kiểm tra và một số tham số. Hy vọng nó sẽ cung cấp một điểm khởi đầu tốt.


Cảm ơn đã bao gồm các liên kết để hiểu làm thế nào điều này hoạt động. Viết trên sqlservercentral là tuyệt vời!
Chris Porter

20

Tất cả tín dụng cho Bogdan Maxim & Peter Mortensen. Đây là bài đăng của họ, tôi vừa thêm ngày lễ vào hàm (Điều này giả sử bạn có một bảng "tblHoliday" với trường datetime "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)
*/

2
Xin chào B. Chỉ để cho bạn biết rằng phiên bản của bạn giả định rằng bảng tblHoliday không chứa Thứ Bảy và Thứ Hai, điều này, đôi khi xảy ra. Dù sao, cảm ơn vì đã chia sẻ phiên bản của bạn. Chúc mừng
Julio Nobre

3
Julio - Có - Phiên bản của tôi cho rằng Thứ Bảy và Chủ Nhật (không phải Thứ Hai) là ngày cuối tuần và không phải là ngày "không kinh doanh". Nhưng nếu bạn làm việc vào cuối tuần, thì tôi đoán mỗi ngày là "ngày làm việc" và bạn có thể nhận xét phần thứ bảy & chủ nhật của điều khoản và chỉ cần thêm tất cả các ngày nghỉ của bạn vào bảng tblHoliday.
Dân B

1
Cảm ơn Dân. Tôi đã kết hợp chức năng này vào chức năng của mình, thêm một kiểm tra vào cuối tuần vì bảng DateDimensions của tôi bao gồm tất cả các ngày, ngày lễ, v.v. Lấy chức năng của bạn, tôi vừa thêm: và IsWeekend = 0 sau khi [HolDate] giữa StartDate và EndDate)
Ngoài ra,

Nếu bảng Holiday chứa các ngày lễ vào cuối tuần, bạn có thể sửa đổi các tiêu chí như thế này: WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6chỉ tính các ngày lễ từ thứ Hai đến thứ Sáu.
Andre

7

Một cách tiếp cận khác để tính toán ngày làm việc là sử dụng vòng lặp WHILE, về cơ bản lặp lại trong phạm vi ngày và tăng nó lên 1 bất cứ khi nào ngày được tìm thấy trong Thứ Hai - Thứ Sáu. Tập lệnh hoàn chỉnh để tính ngày làm việc bằng cách sử dụng vòng lặp WHILE được hiển thị bên dưới:

CREATE FUNCTION [dbo].[fn_GetTotalWorkingDaysUsingLoop]
(@DateFrom DATE,
@DateTo   DATE
)
RETURNS INT
AS
     BEGIN
         DECLARE @TotWorkingDays INT= 0;
         WHILE @DateFrom <= @DateTo
             BEGIN
                 IF DATENAME(WEEKDAY, @DateFrom) IN('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday')
                     BEGIN
                         SET @TotWorkingDays = @TotWorkingDays + 1;
                 END;
                 SET @DateFrom = DATEADD(DAY, 1, @DateFrom);
             END;
         RETURN @TotWorkingDays;
     END;
GO

Mặc dù tùy chọn vòng lặp WHILE sạch hơn và sử dụng ít dòng mã hơn, nhưng nó có khả năng trở thành nút cổ chai hiệu năng trong môi trường của bạn, đặc biệt khi phạm vi ngày của bạn kéo dài trong vài năm.

Bạn có thể xem thêm các phương pháp về cách tính ngày và giờ làm việc trong bài viết này: https://www.sqlshack.com/how-to-calculate-work-days-and-hours-in-sql-server/


6

Phiên bản câu trả lời được chấp nhận của tôi dưới dạng hàm sử dụng DATEPART, vì vậy tôi không phải thực hiện so sánh chuỗi trên dòng với

DATENAME(dw, @StartDate) = 'Sunday'

Dù sao, đây là chức năng hẹn hò kinh doanh của tôi

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

5
 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

 RETURN @WorkDays

Nếu bạn đăng các mẫu mã, XML hoặc dữ liệu, vui lòng tô sáng các dòng đó trong trình soạn thảo văn bản và nhấp vào nút "mẫu mã" ({}) trên thanh công cụ của trình soạn thảo để định dạng độc đáo và cú pháp tô sáng nó!
marc_s

Tuyệt vời, không cần các chức năng ngoại vi hoặc cập nhật cơ sở dữ liệu bằng cách sử dụng này. Cảm ơn. Yêu btw Saltire :-)
Brian Scott

Giải pháp siêu. Tôi đã chìm trong các công thức cho các biến được sử dụng trong Vũ trụ webi để tính các ngày trong tuần (MF) giữa các ngày trong 2 cột của bảng như vậy ... ((((DATEDIFF (day, table.col1, table.col2) +1) - ((CASE DATENAME (ngày trong tuần, bảng.col2) KHI 'Thứ bảy' THÌ 1 KHI 'Chủ nhật' THÌ 2 ELSE 0 END)) / 7) * 5) + (((DATEDIFF (ngày, bảng.col1, bảng.col2 ) +1) - ((CASE DATENAME (ngày trong tuần, bảng.col2) KHI 'Thứ bảy' THÌ 1 KHI 'Chủ nhật' THÌ 2 ELSE 0 END))% 7)
Hilary

5

(Tôi có vài điểm ngại nhận xét về đặc quyền)

Nếu bạn quyết định từ bỏ ngày +1 trong giải pháp tao nhã của CMS , hãy lưu ý rằng nếu ngày bắt đầu và ngày kết thúc của bạn diễn ra trong cùng một ngày cuối tuần, bạn sẽ nhận được câu trả lời tiêu cực. Tức là, 2008/10/26 đến 2008/10/26 trả về -1.

giải pháp khá đơn giản của tôi:

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

.. cũng đặt tất cả các bài viết sai với ngày bắt đầu sau ngày kết thúc bằng không. Một cái gì đó bạn có thể hoặc không thể tìm kiếm.


5

Để biết sự khác biệt giữa các ngày kể cả ngày lễ, tôi đã đi theo cách này:

1) Bảng có ngày lễ:

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

2) Tôi đã có Bảng quy hoạch của mình như thế này và muốn điền vào cột Work_Days trống:

    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) Vì vậy, để có được "Work_Days" để sau đó điền vào cột của tôi, tôi phải:

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

Hy vọng rằng tôi có thể giúp đỡ.

Chúc mừng


1
Liên quan đến phép trừ ngày lễ của bạn. Nếu ngày bắt đầu là ngày 1 tháng 1 và ngày kết thúc là ngày 31 tháng 12 thì sao? Bạn sẽ trừ chỉ 2 - đó là sai. Tôi đề xuất sử dụng DATEDIFF (ngày, Start_Date, Ngày) và tương tự cho End_Date thay vì toàn bộ 'CHỌN COUNT (*) TỪ ngày lễ ...'.
Illia Ratkevych

4

Đây là một phiên bản hoạt động tốt (tôi nghĩ). Bảng ngày lễ chứa các cột Holiday_date chứa các ngày lễ mà công ty bạn quan sát.

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 

Những ngày nghỉ lễ cũng có thể rơi vào cuối tuần. Và đối với một số người, kỳ nghỉ vào Chủ nhật sẽ được thay thế vào thứ Hai tới.
IINA Soetomo 2/11/2016

3

Tôi biết đây là một câu hỏi cũ nhưng tôi cần một công thức cho ngày làm việc trừ ngày bắt đầu vì tôi có một vài mục và cần ngày để tích lũy chính xác.

Không có câu trả lời không lặp nào làm việc cho tôi.

Tôi đã sử dụng một định nghĩa như

Số lần từ nửa đêm đến thứ hai, thứ ba, thứ tư, thứ năm và thứ sáu được thông qua

(những người khác có thể tính từ nửa đêm đến thứ bảy thay vì thứ hai)

Tôi đã kết thúc với công thức này

SELECT DATEDIFF(day, @StartDate, @EndDate) /* all midnights passed */
     - DATEDIFF(week, @StartDate, @EndDate) /* remove sunday midnights */
     - DATEDIFF(week, DATEADD(day, 1, @StartDate), DATEADD(day, 1, @EndDate)) /* remove saturday midnights */

1
Cái đó đã làm cho tôi nhưng tôi phải làm một thay đổi nhỏ. Nó không được tính khi nào @StartDatelà thứ bảy hoặc thứ sáu. Đây là phiên bản của tôi:DATEDIFF(day, @StartDate, @EndDate) - DATEDIFF(week, @StartDate, @EndDate) - DATEDIFF(week, DATEADD(day, 1, @StartDate), DATEADD(day, 1, @EndDate)) - (CASE WHEN DATEPART(WEEKDAY, @StartDate) IN (1, 7) THEN 1 ELSE 0 END) + 1
caiosm1005

@ caiosm1005, thứ bảy đến chủ nhật trả về 0, thứ bảy đến thứ hai trả về 1, thứ sáu đến thứ bảy trả về 0. Tất cả đều phù hợp với định nghĩa của tôi. Mã của bạn sẽ không tích lũy chính xác (ví dụ: trả lại 6 cho thứ Sáu đến thứ Sáu nhưng 5 cho thứ hai đến thứ hai)
adrianm

3

Về cơ bản, đây là câu trả lời của CMS mà không phụ thuộc vào một cài đặt ngôn ngữ cụ thể. Và vì chúng tôi đang chụp chung chung, điều đó có nghĩa là nó cũng hoạt động cho tất cả các @@datefirstcài đặt.

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, ...) luôn luôn sử dụng ranh giới từ thứ Bảy đến Chủ Nhật trong nhiều tuần, do đó biểu thức mang tính quyết định và không cần phải sửa đổi (miễn là định nghĩa của chúng tôi về các ngày trong tuần là từ thứ Hai đến thứ Sáu.) Việc đánh số ngày thay đổi tùy theo @@datefirst cài đặt và tính toán sửa đổi xử lý sự điều chỉnh này với sự phức tạp nhỏ của một số số học mô-đun.

Một cách sạch hơn để đối phó với điều thứ bảy / chủ nhật là dịch các ngày trước khi trích xuất một giá trị trong ngày. Sau khi thay đổi, các giá trị sẽ trở lại thẳng hàng với cách đánh số cố định (và có thể quen thuộc hơn) bắt đầu bằng 1 vào Chủ nhật và kết thúc bằng 7 vào Thứ Bảy.

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

Tôi đã theo dõi hình thức của giải pháp này ít nhất là cho đến năm 2002 và một bài báo của Itzik Ben-Gan. ( https://technet.microsoft.com/en-us/l Library / aa175781 ( v = sql.80 ) .aspx) Mặc dù nó cần một tinh chỉnh nhỏ kể từ khi mới hơndate loại không cho phép số học ngày, nhưng nó lại giống hệt nhau.

EDIT: Tôi đã thêm lại cái +1mà bằng cách nào đó đã bị bỏ lại. Cũng đáng lưu ý rằng phương pháp này luôn tính ngày bắt đầu và ngày kết thúc. Nó cũng giả định rằng ngày kết thúc là vào hoặc sau ngày bắt đầu.


Lưu ý rằng điều này sẽ trả về kết quả sai trong nhiều ngày vào cuối tuần để họ không thêm upp (Fri-> Mon nên giống như Fri-> Sat + Sat-> Sun + Sun-> Mon). Thứ sáu-> Sat nên là 0 (đúng), Sat-> Sun nên là 0 (sai -1), Sun-> Mon nên là 1 (sai 0). Các lỗi khác sau đây là Sat-> Sat = -1, Sun-> Sun = -1, Sun-> Sat = 4
adrianm

@adrianm Tôi tin rằng tôi đã sửa chữa các vấn đề. Trên thực tế, vấn đề là nó luôn bị tắt bởi vì tôi đã vô tình làm rơi phần đó một cách tình cờ.
Shawnt00

Cảm ơn các cập nhật. Tôi nghĩ rằng công thức của bạn đã loại trừ ngày bắt đầu, đó là những gì tôi cần. Tự giải quyết nó và thêm nó như một câu trả lời khác.
adrianm

2

Sử dụng bảng ngày:

    DECLARE 
        @StartDate date = '2014-01-01',
        @EndDate date = '2014-01-31'; 
    SELECT 
        COUNT(*) As NumberOfWeekDays
    FROM dbo.Calendar
    WHERE CalendarDate BETWEEN @StartDate AND @EndDate
      AND IsWorkDay = 1;

Nếu bạn không có điều đó, bạn có thể sử dụng bảng số:

    DECLARE 
    @StartDate datetime = '2014-01-01',
    @EndDate datetime = '2014-01-31'; 
    SELECT 
    SUM(CASE WHEN DATEPART(dw, DATEADD(dd, Number-1, @StartDate)) BETWEEN 2 AND 6 THEN 1 ELSE 0 END) As NumberOfWeekDays
    FROM dbo.Numbers
    WHERE Number <= DATEDIFF(dd, @StartDate, @EndDate) + 1 -- Number table starts at 1, we want a 0 base

Cả hai nên nhanh chóng và nó đưa ra sự mơ hồ / phức tạp. Tùy chọn đầu tiên là tốt nhất nhưng nếu bạn không có bảng lịch, bạn luôn có thể tạo bảng số bằng CTE.


1
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

Nếu bạn định sử dụng một hàm, có thể tốt hơn là sử dụng hàm dựa trên bảng như trong câu trả lời của Mário Meyrelles
James Jenkins

1
CREATE FUNCTION x
(
    @StartDate DATETIME,
    @EndDate DATETIME
)
RETURNS INT
AS
BEGIN
    DECLARE @Teller INT

    SET @StartDate = DATEADD(dd,1,@StartDate)

    SET @Teller = 0
    IF DATEDIFF(dd,@StartDate,@EndDate) <= 0
    BEGIN
        SET @Teller = 0 
    END
    ELSE
    BEGIN
        WHILE
            DATEDIFF(dd,@StartDate,@EndDate) >= 0
        BEGIN
            IF DATEPART(dw,@StartDate) < 6
            BEGIN
                SET @Teller = @Teller + 1
            END
            SET @StartDate = DATEADD(dd,1,@StartDate)
        END
    END
    RETURN @Teller
END

1

Tôi đã lấy các ví dụ khác nhau ở đây, nhưng trong tình huống cụ thể của tôi, chúng tôi có @PromisedDate để giao hàng và @ReceuredDate cho việc nhận thực tế của mặt hàng. Khi một mục được nhận trước "PromisedDate", các phép tính không hoàn toàn chính xác trừ khi tôi đặt hàng ngày được chuyển vào hàm theo thứ tự lịch. Không muốn kiểm tra ngày mỗi lần, tôi đã thay đổi chức năng để xử lý việc này cho tôi.

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

1

Nếu bạn cần thêm ngày làm việc vào một ngày nhất định, bạn có thể tạo một hàm phụ thuộc vào bảng lịch, được mô tả dưới đây:

CREATE TABLE Calendar
(
  dt SMALLDATETIME PRIMARY KEY, 
  IsWorkDay BIT
);

--fill the rows with normal days, weekends and holidays.


create function AddWorkingDays (@initialDate smalldatetime, @numberOfDays int)
    returns smalldatetime as 

    begin
        declare @result smalldatetime
        set @result = 
        (
            select t.dt from
            (
                select dt, ROW_NUMBER() over (order by dt) as daysAhead from calendar 
                where dt > @initialDate
                and IsWorkDay = 1
                ) t
            where t.daysAhead = @numberOfDays
        )

        return @result
    end

+1 Tôi đã kết thúc bằng cách sử dụng một giải pháp tương tự ở đây
James Jenkins

1

Như với DATEDIFF, tôi không coi ngày kết thúc là một phần của khoảng thời gian. Số lượng Chủ nhật (ví dụ) giữa @StartDate và @EndDate là số Chủ nhật giữa Thứ Hai "ban đầu" và @EndDate trừ đi số Chủ nhật giữa Thứ Hai "ban đầu" này và @StartDate. Biết được điều này, chúng ta có thể tính số ngày làm việc như sau:

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SET @StartDate = '2018/01/01'
SET @EndDate = '2019/01/01'

SELECT DATEDIFF(Day, @StartDate, @EndDate) -- Total Days
  - (DATEDIFF(Day, 0, @EndDate)/7 - DATEDIFF(Day, 0, @StartDate)/7) -- Sundays
  - (DATEDIFF(Day, -1, @EndDate)/7 - DATEDIFF(Day, -1, @StartDate)/7) -- Saturdays

Trân trọng!


Hoàn hảo! Đây là những gì tôi đang tìm kiếm. Cảm tạ!
Phantom

0

Đó là làm việc cho tôi, ở nước tôi vào thứ bảy và chủ nhật là những ngày không làm việc.

Đối với tôi là thời gian quan trọng của @StartDate và @EndDate.

CREATE FUNCTION [dbo].[fnGetCountWorkingBusinessDays]
(
    @StartDate as DATETIME,
    @EndDate as DATETIME
)
RETURNS INT
AS
BEGIN
    DECLARE @res int

SET @StartDate = CASE 
    WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN DATEADD(dd, 2, DATEDIFF(dd, 0, @StartDate))
    WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN DATEADD(dd, 1, DATEDIFF(dd, 0, @StartDate))
    ELSE @StartDate END

SET @EndDate = CASE 
    WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN DATEADD(dd, 0, DATEDIFF(dd, 0, @EndDate))
    WHEN DATENAME(dw, @EndDate) = 'Sunday' THEN DATEADD(dd, -1, DATEDIFF(dd, 0, @EndDate))
    ELSE @EndDate END


SET @res =
    (DATEDIFF(hour, @StartDate, @EndDate) / 24)
  - (DATEDIFF(wk, @StartDate, @EndDate) * 2)

SET @res = CASE WHEN @res < 0 THEN 0 ELSE @res END

    RETURN @res
END

GO

0

Tạo chức năng như:

CREATE FUNCTION dbo.fn_WorkDays(@StartDate DATETIME, @EndDate DATETIME= NULL )
RETURNS INT 
AS
BEGIN
       DECLARE @Days int
       SET @Days = 0

       IF @EndDate = NULL
              SET @EndDate = EOMONTH(@StartDate) --last date of the month

       WHILE DATEDIFF(dd,@StartDate,@EndDate) >= 0
       BEGIN
              IF DATENAME(dw, @StartDate) <> 'Saturday' 
                     and DATENAME(dw, @StartDate) <> 'Sunday' 
                     and Not ((Day(@StartDate) = 1 And Month(@StartDate) = 1)) --New Year's Day.
                     and Not ((Day(@StartDate) = 4 And Month(@StartDate) = 7)) --Independence Day.
              BEGIN
                     SET @Days = @Days + 1
              END

              SET @StartDate = DATEADD(dd,1,@StartDate)
       END

       RETURN  @Days
END

Bạn có thể gọi hàm như:

select dbo.fn_WorkDays('1/1/2016', '9/25/2016')

Hoặc thích:

select dbo.fn_WorkDays(StartDate, EndDate) 
from table1

0
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

Kết thúc


0

Tôi thấy TSQL dưới đây là một giải pháp khá thanh lịch (tôi không có quyền để chạy các chức năng). Tôi tìm thấy DATEDIFFbỏ quaDATEFIRST và tôi muốn ngày đầu tiên trong tuần của tôi là thứ Hai. Tôi cũng muốn ngày làm việc đầu tiên được đặt thành số 0 và nếu nó rơi vào cuối tuần thứ Hai sẽ là số không. Điều này có thể giúp một người có yêu cầu hơi khác :)

Nó không xử lý ngày lễ ngân hàng

SET DATEFIRST 1
SELECT
,(DATEDIFF(DD,  [StartDate], [EndDate]))        
-(DATEDIFF(wk,  [StartDate], [EndDate]))        
-(DATEDIFF(wk, DATEADD(dd,-@@DATEFIRST,[StartDate]), DATEADD(dd,-@@DATEFIRST,[EndDate]))) AS [WorkingDays] 
FROM /*Your Table*/ 

0

Một cách tiếp cận là "đi bộ ngày" từ đầu đến cuối kết hợp với biểu thức trường hợp kiểm tra xem ngày đó không phải là thứ bảy hay chủ nhật và gắn cờ (1 cho ngày trong tuần, 0 cho cuối tuần). Và cuối cùng chỉ là các cờ tổng (nó sẽ bằng với số lượng 1 cờ vì cờ khác là 0) để cung cấp cho bạn số ngày trong tuần.

Bạn có thể sử dụng loại hàm tiện ích GetNums (startNumber, endNumber) để tạo ra một chuỗi các số để 'lặp' từ ngày bắt đầu đến ngày kết thúc. Tham khảo http://tsql.solidq.com/SourceCodes/GetNums.txt để triển khai. Logic cũng có thể được mở rộng để phục vụ cho các ngày lễ (giả sử nếu bạn có một bảng ngày lễ)

declare @date1 as datetime = '19900101'
declare @date2 as datetime = '19900120'

select  sum(case when DATENAME(DW,currentDate) not in ('Saturday', 'Sunday') then 1 else 0 end) as noOfWorkDays
from dbo.GetNums(0,DATEDIFF(day,@date1, @date2)-1) as Num
cross apply (select DATEADD(day,n,@date1)) as Dates(currentDate)

0

Tôi đã mượn một số ý tưởng từ những người khác để tạo ra giải pháp của tôi. Tôi sử dụng mã nội tuyến để bỏ qua các ngày cuối tuần và ngày lễ liên bang Hoa Kỳ. Trong môi trường của tôi, EndDate có thể là null, nhưng nó sẽ không bao giờ đứng trước 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.