我有一个函数可以计算两个日期之间的工作日数。我想要的是将这个现有的函数转换成类似于计算月份假日的数量或让我们说两个日期之间的函数。
我现有的功能是这样的。
CREATE FUNCTION [dbo].[fnGetDaysWorked] (@StartDate datetime, @EndDate datetime)
RETURNS int
AS
BEGIN
DECLARE @dateFrom datetime = '2018/01/01'
DECLARE @dateTo datetime = '2018/01/31'
SET @StartDate = @dateFrom
SET @EndDate = @dateTo
DECLARE @WORKDAYS INT
SELECT @WORKDAYS = (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)
RETURN @WORKDAYS
END
还有一件事,我无法处理像独立日这样的某个月有某种公众假期的情况。我有一个名为日历的表来处理这种情况,但我无法在此集成。表格结构如下。
CREATE TABLE Calendar
(
ID Int,
[DayName] Varchar(25),
Holiday datetime,
PublicHoliday Binary,
OffDay Binary,
Active Binary
);
任何帮助都将不胜感激。
答案 0 :(得分:1)
这将计算两个日期之间的星期六和星期日的数量:
DECLARE @from date = '2018-02-03'
DECLARE @to date = '2018-02-13'
SELECT
datediff(day, -2, @to)/7-datediff(day, -1, @from)/7 -- saturdays
+ datediff(day, -1, @to)/7-datediff(day, 0, @from)/7 -- sundays
假期因国家/地区而异。
此功能将决定复活节何时开始特定年份:
CREATE FUNCTION [dbo].[f_EasterByYear] (@Year SMALLINT) RETURNS DATE AS
BEGIN
DECLARE
@c INT,
@n INT,
@k INT,
@i INT,
@j INT,
@l INT,
@m INT,
@d INT,
@Easter DATE
SET @c = (@Year / 100)
SET @n = @Year - 19 * (@Year / 19)
SET @k = (@c - 17) / 25
SET @i = @c - @c / 4 - ( @c - @k) / 3 + 19 * @n + 15
SET @i = @i - 30 * ( @i / 30 )
SET @i = @i - (@i / 28) * (1 - (@i / 28) *
(29 / (@i + 1)) * ((21 - @n) / 11))
SET @j = @Year + @Year / 4 + @i + 2 - @c + @c / 4
SET @j = @j - 7 * (@j / 7)
SET @l = @i - @j
SET @m = 3 + (@l + 40) / 44
SET @d = @l + 28 - 31 * ( @m / 4 )
SET @Easter = DATEADD(month, @m, DATEADD(year, @Year- 1900,
DATEADD(d, @d, '1899-11-30')))
RETURN @Easter
END
此功能将在工作日返回1,否则返回0:
CREATE FUNCTION [dbo].[f_IsWorkDay] (@Date DATE) RETURNS BIT AS
BEGIN
DECLARE @IsWorkDay BIT = 1
SELECT @IsWorkDay =
CASE
--Exclude Saturday and Sunday (Datefirst-independent)
WHEN DATEDIFF(d, 0, @Date) % 7 IN (5,6) THEN 0
WHEN MONTH(@Date)=12 AND DAY(@Date) IN (24,25,26,31) THEN 0
--Exclude New Years
WHEN MONTH(@Date)=1 AND DAY(@Date)=1 THEN 0
--Exclude Easter, Pentecost and Ascension of Jesus
WHEN @Date IN (
DATEADD(DAY,-3,dbo.f_EasterByYear(YEAR(@Date))),
DATEADD(DAY,-2,dbo.f_EasterByYear(YEAR(@Date))),
DATEADD(DAY,1, dbo.f_EasterByYear(YEAR(@Date))),
DATEADD(DAY,26,dbo.f_EasterByYear(YEAR(@Date))),
DATEADD(DAY,39,dbo.f_EasterByYear(YEAR(@Date))),
DATEADD(DAY,50,dbo.f_EasterByYear(YEAR(@Date)))
) THEN 0
ELSE 1
END
RETURN @IsWorkDay
END
您可以通过这种方式测试某一天是否是工作日:
SELECT dbo.f_Isworkday(date) isWorkDay, date
FROM (values('2018-02-09'),('2018-02-10'),('2018-02-11'),('2018-02-12')) x(date)
高级测试给出输入间隔:
DECLARE
@from DATE = '2018-02-09',
@to DATE = '2018-02-13'
;WITH N(N)AS
(SELECT 1 FROM(VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))M(N)),
tally(N)AS(SELECT ROW_NUMBER()OVER(ORDER BY N.N)FROM N,N a,N b,N c,N d,N e)
SELECT
dbo.f_Isworkday(dateadd(d, N-1, @from)) isworkday,
dateadd(d, N-1, @from) date
FROM tally
WHERE N <= datediff(d, @from, @to) + 1
答案 1 :(得分:0)
使用此功能,它也适用于您的日历表。 “日历”表中的所有“活动”条目都不会计为工作日
CREATE FUNCTION [dbo].[fnGetDaysWorked2] (@StartDate datetime, @EndDate datetime)
RETURNS int
BEGIN
DECLARE @tblNonWorking TABLE(dt DATETIME,dtw varchar(30))
While (@StartDate<=@EndDate)
BEGIN
INSERT INTO @tblNonWorking(dt,dtw) VALUES (@StartDate,Lower(DATENAME(dw, @StartDate)))
SET @StartDate=Dateadd(day,1,@StartDate)
END
DECLARE @WORKDAYS INT
SET @WORKDAYS=0
SELECT @WORKDAYS=COUNT(1)
FROM @tblNonWorking a
LEFT JOIN Calendar c ON a.dt=c.Holiday AND c.Active=1
WHERE a.dtw NOT IN ('saturday','sunday')
AND c.ID IS NULL
RETURN @WORKDAYS
END