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.
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.
Câu trả lời:
Đố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 ...
-(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)
+(CASE WHEN DATENAME(dw, @StartDate) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
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.
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)
*/
WHERE HolDate BETWEEN @StartDate AND @EndDate AND DATEPART(dw, HolDate) BETWEEN 2 AND 6
chỉ tính các ngày lễ từ thứ Hai đến thứ Sáu.
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/
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
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
(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.
Để 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
Đâ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
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 */
@StartDate
là 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
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 @@datefirst
cà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 +1
mà 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.
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.
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
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
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
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
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!
Đó 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
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
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
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 DATEDIFF
bỏ 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*/
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)
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