计算sql

时间:2018-02-13 08:12:21

标签: sql-server

我有一个函数可以计算两个日期之间的工作日数。我想要的是将这个现有的函数转换成类似于计算月份假日的数量或让我们说两个日期之间的函数。

我现有的功能是这样的。

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

任何帮助都将不胜感激。

2 个答案:

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