sql跳过非工作日(周末和节假日)

简介:场景1:基于开始日期和工期,推算结束日期。 场景2:基于开始日期和结束日期,计算工期 注:需要自己做界面维护工作日表(s_WorkDay)和节假日表(s_SpecialDay) 

涉及到的数据表

IF OBJECT_ID('s_WorkDay') IS NULL 
BEGIN 
     CREATE TABLE s_WorkDay([Monday] [TINYINT] 
          ,[Tuesday] [TINYINT] 
          ,[Wednesday] [TINYINT] 
          ,[Thursday] [TINYINT] 
          ,[Friday] [TINYINT] 
          ,[Saturday] [TINYINT] 
          ,[Sunday] [TINYINT] 
     )
END
GO

IF OBJECT_ID('s_SpecialDay') IS NULL 
BEGIN 
     CREATE TABLE s_SpecialDay([SpecialDayGUID] [UNIQUEIDENTIFIER] PRIMARY KEY CLUSTERED NOT NULL  
          ,[BeginDate] [DATETIME] 
          ,[EndDate] [DATETIME] 
          ,[IsWorkDay] [TINYINT] 
          ,[Remarks] [VARCHAR](200) 
     )
END
GO

场景1:根据开始日期和工期,计算结束日期

--根据开始日期推出结束日期

IF EXISTS ( SELECT  * FROM    dbo.sysobjects WHERE   id = OBJECT_ID(N'[dbo].[fn_GetEndDate]') AND xtype IN ( N'FN', N'IF', N'TF' ) )

    DROP FUNCTION [dbo].[fn_GetEndDate]

GO

CREATE   FUNCTION fn_GetEndDate ( @date DATETIME,@Duration INT )

RETURNS DATETIME

AS

    BEGIN

        DECLARE @Edate DATETIME

        DECLARE @IsAdd INT

        SET @Edate=@date

        SET @Duration=@Duration-1

         DECLARE @NoWorkDay TABLE(iDay int)

        --非工作日枚举

        INSERT INTO @NoWorkDay(iDay) SELECT * FROM dbo.fn_NoWorkDay()

       

        WHILE ( @Duration > 0 )

            BEGIN

                SET @IsAdd=0

                --默认往后+1

                SET @Edate=DATEADD(day, 1, @Edate)

                --如果非工作日,重复循环,否则跳下一步

                IF EXISTS( SELECT TOP 1 1 FROM s_workDay WHERE DATEPART(weekday, @Edate) NOT IN (SELECT * FROM @NoWorkDay))

                BEGIN

                 SET @IsAdd=1

                 SET @Duration = @Duration - 1

                END

                --如果当前日期在特殊非工作日中,则不跳

                IF EXISTS( SELECT TOP 1 1 FROM s_SpecialDay WHERE (@Edate BETWEEN begindate AND enddate) AND IsWorkDay=0 AND @IsAdd=1)

                BEGIN

                  SET @Duration = @Duration + 1

                END

                --如果当前日期在特殊工作日中,则跳1

                  IF EXISTS( SELECT TOP 1 1 FROM s_SpecialDay WHERE (@Edate BETWEEN begindate and enddate) AND IsWorkDay=1 AND @IsAdd=0)

                BEGIN

                  SET @Duration = @Duration -1

                END

            END

        RETURN @Edate

    END

GO

场景2:根据两个日期,计算工期

--计算工期

IF EXISTS ( SELECT  * FROM    dbo.sysobjects WHERE   id = OBJECT_ID(N'[dbo].[fn_GetDuration]') AND xtype IN ( N'FN', N'IF', N'TF' ) )

    DROP FUNCTION [dbo].[fn_GetDuration]

GO

CREATE   FUNCTION fn_GetDuration(@BeginDate DATETIME,@EndDate DATETIME)

RETURNS INT

AS

    BEGIN

        DECLARE @iCount INT

        --A:取出常规工作日

        SELECT  @iCount = ISNULL(dbo.fn_GetWorkDay(@BeginDate, @EndDate), 0) 

       

        --B:减去特殊非工作日

        SELECT  @iCount = @iCount

                - ISNULL(SUM(dbo.fn_GetWorkDay(CASE WHEN begindate < @BeginDate

                                                    THEN @BeginDate

                                                    ELSE begindate

                                               END,

                                               CASE WHEN enddate > @EndDate

                                                    THEN @EndDate

                                                    ELSE enddate

                                               END)), 0)

        FROM    s_SpecialDay

        WHERE   isworkday = 0

                AND SpecialDayGUID NOT IN (

                SELECT  SpecialDayGUID

                FROM    s_SpecialDay

                WHERE   ( begindate > @EndDate

                          AND enddate > @EndDate

                        )

                        OR ( begindate < @BeginDate

                             AND enddate < @BeginDate

                           ) )

     

        ----C:加上特殊工作日

        SELECT  @iCount = @iCount + ISNULL(SUM(DATEDIFF(dd,

                                                        CASE WHEN begindate < @BeginDate

                                                             THEN @BeginDate

                                                             ELSE begindate

                                                        END,

                                                        CASE WHEN enddate > @EndDate

                                                             THEN @EndDate

                                                             ELSE enddate

                                                        END)

                                               - dbo.fn_GetWorkDay(CASE

                                                              WHEN begindate < @BeginDate

                                                              THEN @BeginDate

                                                              ELSE begindate

                                                              END,

                                                              CASE

                                                              WHEN enddate > @EndDate

                                                              THEN @EndDate

                                                              ELSE enddate

                                                              END)), 0)

        FROM    s_SpecialDay

        WHERE   isworkday = 1

                AND SpecialDayGUID NOT IN (

                SELECT  SpecialDayGUID

                FROM    s_SpecialDay

                WHERE   ( begindate > @EndDate

                          AND enddate > @EndDate

                        )

                        OR ( begindate < @BeginDate

                             AND enddate < @BeginDate

                           ) )

        RETURN @iCount

    END

GO

需要用到的函数

IF EXISTS ( SELECT  *

            FROM    dbo.sysobjects

            WHERE   id = OBJECT_ID(N'[dbo].[fn_NoWorkDay]')

                    AND xtype IN ( N'FN', N'IF', N'TF' ) )

    DROP FUNCTION [dbo].[fn_NoWorkDay]

GO

--输出非工作日

CREATE   FUNCTION fn_NoWorkDay()

RETURNS @NoWorkDay TABLE ( iDay INT )

AS

    BEGIN

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  2

                FROM    s_WorkDay

                WHERE   Monday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  3

                FROM    s_WorkDay

                WHERE   Tuesday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  4

                FROM    s_WorkDay

                WHERE   Wednesday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  5

                FROM    s_WorkDay

                WHERE   Thursday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  6

                FROM    s_WorkDay

                WHERE   Friday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  7

                FROM    s_WorkDay

                WHERE   Saturday = 0

        INSERT  INTO @NoWorkDay

                ( iDay

                )

                SELECT  1

                FROM    s_WorkDay

                WHERE   Sunday = 0

        RETURN 

    END        

 

Go

--计算工作日

IF EXISTS ( SELECT  *

            FROM    dbo.sysobjects

            WHERE   id = OBJECT_ID(N'[dbo].[fn_GetWorkDay]')

                    AND xtype IN ( N'FN', N'IF', N'TF' ) )

    DROP FUNCTION [dbo].[fn_GetWorkDay]

GO

CREATE   FUNCTION fn_GetWorkDay(@BeginDate DATETIME,@EndDate DATETIME)

RETURNS INT

AS

    BEGIN

        DECLARE @NoWorkDay TABLE(iDay int)

        --非工作日枚举

        INSERT INTO @NoWorkDay(iDay) SELECT * FROM fn_NoWorkDay()

        DECLARE @i INT,@iCount INT

        SET @iCount=0

        SET @i = DATEDIFF(day, @BeginDate, @EndDate) 

        WHILE ( @i >= 0 )

            BEGIN

                --如果开始日期往后顺延,遇到非工作日,则不计数

                IF EXISTS( SELECT TOP 1 1 FROM s_workDay WHERE DATEPART(weekday, @BeginDate) NOT IN (SELECT * FROM @NoWorkDay))

                BEGIN

                 SET @iCount=@iCount+1

                END

                SET @BeginDate=DATEADD(day, 1, @BeginDate)

                SET @i = @i - 1 

            END

        RETURN @iCount

    END

go

 

posted @ 2016-07-31 10:47  飘....  阅读(12121)  评论(0编辑  收藏  举报