0

我正在创建函数来返回两个日期之间的工作分钟数。

这会返回确切的分钟数,但是当我在许多记录上使用它时,处理时间很长。

我有 3 个功能:

CREATE FUNCTION FN_FERIES_SELON_ANNEE (@YEAR INT) 
RETURNS @FERIES TABLE (JourId INT NOT NULL, 
                       JourDate DATETIME NOT NULL, 
                       JoURLabel VARCHAR(50) NULL)
AS
BEGIN
    DECLARE @JoursFeries TABLE  (      
        [JourId] [INT] IDENTITY(1,1) NOT NULL,      
        [JourDate] [DATETIME] NOT NULL,      
        [JoURLabel] [VARCHAR](50) NULL 
    )       

    DECLARE @an INT 
    DECLARE @G INT     
    DECLARE @I INT     
    DECLARE @J INT     
    DECLARE @C INT     
    DECLARE @H INT     
    DECLARE @L INT     
    DECLARE @JourPaque INT     
    DECLARE @MoisPaque INT     
    DECLARE @DimPaque DATETIME     
    DECLARE @LunPaque DATETIME     
    DECLARE @JeuAscension DATETIME     
    DECLARE @LunPentecote DATETIME     
    DECLARE @NouvelAn DATETIME     
    DECLARE @FeteTravail DATETIME     
    DECLARE @Armistice3945 DATETIME     
    DECLARE @Assomption DATETIME     
    DECLARE @Armistice1418 DATETIME     
    DECLARE @FeteNationale DATETIME     
    DECLARE @ToussaINT DATETIME     
    DECLARE @Noel DATETIME   

    SET @an = @YEAR 

    SET @G = @an % 19      
    SET @C = @an / 100      
    SET @H = (@C - @C / 4 - (8 * @C + 13) / 25 + 19 * @G + 15) % 30      
    SET @I = @H - (@H / 28) * (1 - (@H / 28) * (29 / (@H + 1)) * ((21 - @G) / 11))      
    SET @J = (@an + @an / 4 + @I + 2 - @C + @C / 4) % 7      

    SET @L = @I - @J      
    SET @MoisPaque = 3 + (@L + 40) / 44      
    SET @JourPaque = @L + 28 - 31 * (@MoisPaque / 4)      

    -- Jours fériés mobiles      

    SET @DimPaque = cast(cast(@an AS VARCHAR(4)) + '-'     
                    + cast(@MoisPaque AS VARCHAR(2)) + '-'     
                    + cast(@JourPaque AS VARCHAR(2)) AS DATETIME)      
    SET @LunPaque = DATEADD(DAY, 1, @DimPaque)      
    SET @JeuAscension = DATEADD(DAY, 39, @DimPaque)      
    SET @LunPentecote = DATEADD(DAY, 50, @DimPaque)      

    -- Jours fériés fixes      

    SET @NouvelAn = cast(cast(@an AS VARCHAR(4))+'-01-01 00:00:00' AS DATETIME)      
    SET @FeteTravail = cast(cast(@an AS VARCHAR(4))+'-05-01 00:00:00' AS DATETIME)      
    SET @Armistice3945 = cast(cast(@an AS VARCHAR(4))+'-05-08 00:00:00' AS DATETIME)      
    SET @Assomption = cast(cast(@an AS VARCHAR(4))+'-08-15 00:00:00' AS DATETIME)      
    SET @Armistice1418 = cast(cast(@an AS VARCHAR(4))+'-11-11 00:00:00' AS DATETIME)      
    SET @FeteNationale = cast(cast(@an AS VARCHAR(4))+'-07-14 00:00:00' AS DATETIME)      
    SET @ToussaINT = cast(cast(@an AS VARCHAR(4))+'-11-01 00:00:00' AS DATETIME)      
    SET @Noel = cast(cast(@an AS VARCHAR(4))+'-12-25 00:00:00' AS DATETIME)      

    INSERT  INTO @JoursFeries (JourDate, JoURLabel)  
    SELECT  @LunPaque, 'Lundi de Pâques' 
    UNION
    SELECT  @JeuAscension, 'Jeudi de l''Ascension'   
    UNION   
    SELECT  @LunPentecote, 'Lundi de Pentecôte'  
    UNION 
    SELECT  @NouvelAn, 'Nouvel an'  
    UNION 
    SELECT  @FeteTravail, 'Fête du travail'
    UNION   
    SELECT  @Armistice3945, 'Armistice 39-45'
    UNION
    SELECT  @Assomption, 'Assomption'
    UNION 
    SELECT  @FeteNationale, 'Fête Nationale'
    UNION 
    SELECT  @ToussaINT, 'Toussaint'
    UNION 
    SELECT  @Armistice1418, 'Armistice 14-18'  
    UNION 
    SELECT  @Noel, 'Noël'

    INSERT INTO @FERIES
    SELECT * FROM @JoursFeries
    RETURN
END
GO

CREATE FUNCTION FN_JOUR_TRAVAILLE (@Date1 DATETIME)
RETURNS INT AS
BEGIN   
    DECLARE @FLAG INT
    SET @FLAG = 1
    DECLARE @YEAR INT
    SET @YEAR = DATEPART(YEAR, @DATE1)

    IF EXISTS(SELECT * FROM FN_FERIES_SELON_ANNEE(@YEAR) WHERE JourDate = @Date1) BEGIN
        SET @FLAG = 0
    END
    ELSE
        IF DatePart(weekday, @DATE1) = 7 OR DatePart(weekday, @DATE1) = 1 BEGIN
            SET @FLAG = 0
        END

    RETURN @FLAG
END
GO

CREATE FUNCTION FN_DATEDIFF_SELON_HORAIRES_ENTREPRISE2 (@Date1 DATETIME, @Date2 DATETIME)
    RETURNS INT AS
    BEGIN
        DECLARE @NB_Jours INT       
        DECLARE @Cpt INT
        DECLARE @Jours_Travailles INT
        DECLARE @Date1_at_8_am DATETIME
        DECLARE @Date2_at_6_pm DATETIME
        DECLARE @Excedent INT

        SET @NB_Jours = DATEDIFF(day, @Date1, @Date2) + 1
        SET @Cpt = 0
        SET @Jours_Travailles = 0
        SET @Excedent = 0

        SET @Date1_at_8_am = @Date1
        SET @Date1_at_8_am = DATEADD(hour, - (DATEPART(hour, @Date1) - 8), @Date1_at_8_am)
        SET @Date1_at_8_am = DATEADD(minute, - DATEPART(minute, @Date1_at_8_am), @Date1_at_8_am)
        SET @Date1_at_8_am = DATEADD(second, - DATEPART(second, @Date1_at_8_am), @Date1_at_8_am)

        SET @Date2_at_6_pm = @Date2
        SET @Date2_at_6_pm = DATEADD(hour, 18 - DATEPART(hour, @Date2), @Date2_at_6_pm)
        SET @Date2_at_6_pm = DATEADD(minute, - DATEPART(minute, @Date2_at_6_pm), @Date2_at_6_pm)
        SET @Date2_at_6_pm = DATEADD(second, - DATEPART(second, @Date2_at_6_pm), @Date2_at_6_pm)

        IF dbo.FN_JOUR_TRAVAILLE(@Date1) = 1 AND @Date1 > @Date1_at_8_am BEGIN
            SET @Excedent = @Excedent + DATEDIFF(minute, @Date1_at_8_am, @Date1)
        END
        IF dbo.FN_JOUR_TRAVAILLE(@Date2) = 1 AND @Date2 < @Date2_at_6_pm BEGIN
            SET @Excedent = @Excedent + DATEDIFF(minute, @Date2, @Date2_at_6_pm)
        END 

        WHILE @Cpt < @NB_Jours
        BEGIN
            IF dbo.FN_JOUR_TRAVAILLE(DATEADD(day, @Cpt, @Date1)) = 1 
            BEGIN
                SET @Jours_Travailles = @Jours_Travailles + 1
            END
            SET @Cpt = @Cpt + 1
        END

        RETURN @Jours_Travailles*600 - @Excedent
END
GO

-----------------------------------------------------------------------
-----------------------------------------------------------------------
-----------------------------------------------------------------------

问题出在这一行的函数 FN_JOUR_TRAVAILLE 中:

IF EXISTS(SELECT * FROM FN_FERIES_SELON_ANNEE(@YEAR) WHERE JourDate = @Date1) BEGIN
        SET @FLAG = 0
END

对于我计算的每个时期的每一天,我都会生成该年的公共假期表。

最好的解决方案是检查我是否尚未创建此表,是否在其中搜索,如果没有,则创建并存储它。

但我不知道该怎么做。

我需要在上级范围内创建一个表,并将其作为参数传递给我认为的函数。

4

2 回答 2

0

How about a table type parameter:

create type FERIES as table (
    JourId INT NOT NULL, 
    JourDate DATETIME NOT NULL, 
    JoURLabel VARCHAR(50) NULL
)
GO

In your outer scope use the FN_FERIES_SELON_ANNEE function to populate a table value:

declare @var FERIES;

insert into @var
    select * from FN_FERIES_SELON_ANNEE(@YEAR)

and then pass it to your other function to use.

Your other function would become:

CREATE FUNCTION FN_JOUR_TRAVAILLE (@Date1 DATETIME, @table FERIES readonly)
RETURNS INT AS
BEGIN   
    DECLARE @FLAG INT
    SET @FLAG = 1
    DECLARE @YEAR INT
    SET @YEAR = DATEPART(YEAR, @DATE1)

    IF EXISTS(SELECT * FROM @table WHERE JourDate = @Date1) BEGIN
        SET @FLAG = 0
    END
    ELSE
        IF DatePart(weekday, @DATE1) = 7 OR DatePart(weekday, @DATE1) = 1 BEGIN
            SET @FLAG = 0
        END

    RETURN @FLAG
END
GO
于 2013-04-26T10:31:57.143 回答
0

尝试使用 XML -

DECLARE @XML XML

SELECT @XML = (
    SELECT JourId, JourDate, JoURLabel  
    FROM @JoursFeries t
    FOR XML AUTO
)

RETURN @XML

CREATE FUNCTION FN_JOUR_TRAVAILLE (@Date1 DATETIME, @XML XML)
RETURNS INT 
AS BEGIN

    DECLARE @YEAR INT

    SELECT @YEAR = DATEPART(YEAR, @DATE1)

    IF EXISTS(
        SELECT 1
        FROM @XML.nodes('/t') t(p)
        WHERE t.p.value('@JourDate', 'DATETIME') = @Date1
    ) OR DATEPART(weekday, @DATE1) IN (1, 7)     
    BEGIN

        RETURN 0

    END

    RETURN 1

END

更新(2005 年或更高版本):

或者试试这个解决方案 -

ALTER FUNCTION FN_FERIES_SELON_ANNEE (@YEAR INT)
RETURNS XML                    
AS BEGIN

    DECLARE 
          @an VARCHAR(4) 
        , @G INT, @I INT     
        , @J INT, @C INT     
        , @H INT, @L INT     
        , @JourPaque INT     
        , @MoisPaque INT     
        , @DimPaque DATETIME    

    SELECT @an = CAST(@YEAR AS VARCHAR(4)) 

    SELECT 
          @G = @YEAR % 19      
        , @C = @YEAR / 100      
        , @H = (@C - @C / 4 - (8 * @C + 13) / 25 + 19 * @G + 15) % 30      
        , @I = @H - (@H / 28) * (1 - (@H / 28) * (29 / (@H + 1)) * ((21 - @G) / 11))      
        , @J = (@YEAR + @YEAR / 4 + @I + 2 - @C + @C / 4) % 7      
        , @L = @I - @J      
        , @MoisPaque = 3 + (@L + 40) / 44      
        , @JourPaque = @L + 28 - 31 * (@MoisPaque / 4)      
        , @DimPaque = CAST(@an + '-' + CAST(@MoisPaque AS VARCHAR(2)) + '-' + CAST(@JourPaque AS VARCHAR(2)) AS DATETIME)            

    DECLARE @XML XML

    SELECT @XML = (
        SELECT JourId, JourDate, JoURLabel  
        FROM (
            SELECT JourId = 1, JourDate = DATEADD(DAY, 1, @DimPaque), JoURLabel = 'Lundi de Pâques' 
                UNION ALL
            SELECT 2, DATEADD(DAY, 39, @DimPaque), 'Jeudi de l''Ascension'   
                UNION ALL   
            SELECT 3, DATEADD(DAY, 50, @DimPaque), 'Lundi de Pentecôte'  
                UNION ALL 
            SELECT 4, CAST(@an + '0101' AS DATETIME), 'Nouvel an'  
                UNION ALL 
            SELECT 5, CAST(@an + '0501' AS DATETIME), 'Fête du travail'
                UNION ALL   
            SELECT 6, CAST(@an + '0508' AS DATETIME), 'Armistice 39-45'
                UNION ALL
            SELECT 7, CAST(@an + '0815' AS DATETIME), 'Assomption'
                UNION ALL 
            SELECT 8, CAST(@an + '0714' AS DATETIME), 'Fête Nationale'
                UNION ALL 
            SELECT 9, CAST(@an + '1101' AS DATETIME), 'Toussaint'
                UNION ALL 
            SELECT 10, CAST(@an + '1111' AS DATETIME), 'Armistice 14-18'  
                UNION ALL 
            SELECT 11, CAST(@an + '1101' AS DATETIME), 'Noël'
        ) t
        FOR XML AUTO
    )

    RETURN @XML

END
GO

ALTER FUNCTION FN_JOUR_TRAVAILLE (@Date1 DATETIME, @XML XML)
RETURNS INT 
AS BEGIN

    DECLARE @YEAR INT
    SELECT @YEAR = DATEPART(YEAR, @DATE1)

    IF EXISTS(
        SELECT 1
        FROM @XML.nodes('/t') t(p)
        WHERE t.p.value('@JourDate', 'DATETIME') = @Date1
    ) OR DATEPART(weekday, @DATE1) IN (1, 7)     
    BEGIN

        RETURN 0

    END

    RETURN 1

END
GO

ALTER FUNCTION FN_DATEDIFF_SELON_HORAIRES_ENTREPRISE2 
(
      @Date1 DATETIME
    , @Date2 DATETIME
)
RETURNS INT AS
BEGIN

    DECLARE 
          @NB_Jours INT       
        , @Cpt INT
        , @Jours_Travailles INT
        , @Date1_at_8_am DATETIME
        , @Date2_at_6_pm DATETIME
        , @Excedent INT

    SELECT 
          @NB_Jours = DATEDIFF(DAY, @Date1, @Date2) + 1
        , @Cpt = 0
        , @Jours_Travailles = 0
        , @Excedent = 0
        , @Date1_at_8_am = DATEADD(HOUR, 8, CAST(FLOOR(CAST(@Date1 AS FLOAT)) AS DATETIME))
        , @Date2_at_6_pm = DATEADD(HOUR, 18, CAST(FLOOR(CAST(@Date1 AS FLOAT)) AS DATETIME))

    SELECT @Excedent = @Excedent + 
            CASE WHEN @Date1 > @Date1_at_8_am AND dbo.FN_JOUR_TRAVAILLE(@Date1, dbo.FN_FERIES_SELON_ANNEE(YEAR(@Date1))) = 1
                THEN DATEDIFF(MINUTE, @Date1_at_8_am, @Date1)
                ELSE 0
            END +
            CASE WHEN @Date2 < @Date2_at_6_pm AND dbo.FN_JOUR_TRAVAILLE(@Date2, dbo.FN_FERIES_SELON_ANNEE(YEAR(@Date2))) = 1
                THEN DATEDIFF(MINUTE, @Date2, @Date2_at_6_pm)
                ELSE 0
            END

    ;WITH years AS 
    (
        SELECT cont = 1, dt = DATEADD(DAY, @Cpt, @Date1), years = YEAR(DATEADD(DAY, @Cpt, @Date1))

        UNION ALL

        SELECT cont + 1, DATEADD(DAY, 1, dt), YEAR(DATEADD(DAY, 1, dt))
        FROM years
        WHERE cont < @NB_Jours
    )
    SELECT @Jours_Travailles = SUM(dbo.FN_JOUR_TRAVAILLE(dt, tt.xmls))
    FROM years y
    JOIN (
        SELECT y3.years, xmls = dbo.FN_FERIES_SELON_ANNEE(y3.years)
        FROM (
            SELECT DISTINCT y2.years
            FROM years y2
        ) y3
    ) tt ON tt.years = y.years
    OPTION (MAXRECURSION 0)

    RETURN @Jours_Travailles * 600 - @Excedent

END

更新2(2000):

如果使用 2000 版,似乎有相当多的限制。请试试这个例子。我不认为这是在不改变当前逻辑的情况下解决问题的完美决定,但我认为它应该对您有所帮助。

ALTER FUNCTION FN_FERIES_SELON_ANNEE (@YEAR INT)
RETURNS @FERIES TABLE 
(
      JourId INT NOT NULL
    , JourDate DATETIME NOT NULL
    , JoURLabel VARCHAR(50) NULL
)                 
AS BEGIN

    DECLARE 
          @an VARCHAR(4) 
        , @G INT, @I INT     
        , @J INT, @C INT     
        , @H INT, @L INT     
        , @JourPaque INT     
        , @MoisPaque INT     
        , @DimPaque DATETIME    

    SELECT @an = CAST(@YEAR AS VARCHAR(4)) 

    SELECT 
          @G = @YEAR % 19      
        , @C = @YEAR / 100      
        , @H = (@C - @C / 4 - (8 * @C + 13) / 25 + 19 * @G + 15) % 30      
        , @I = @H - (@H / 28) * (1 - (@H / 28) * (29 / (@H + 1)) * ((21 - @G) / 11))      
        , @J = (@YEAR + @YEAR / 4 + @I + 2 - @C + @C / 4) % 7      
        , @L = @I - @J      
        , @MoisPaque = 3 + (@L + 40) / 44      
        , @JourPaque = @L + 28 - 31 * (@MoisPaque / 4)      
        , @DimPaque = CAST(@an + '-' + CAST(@MoisPaque AS VARCHAR(2)) + '-' + CAST(@JourPaque AS VARCHAR(2)) AS DATETIME)            

    INSERT INTO @FERIES (JourId, JourDate, JoURLabel )
    SELECT JourId, JourDate, JoURLabel  
    FROM (
        SELECT JourId = 1, JourDate = DATEADD(DAY, 1, @DimPaque), JoURLabel = 'Lundi de Pâques' 
            UNION ALL
        SELECT 2, DATEADD(DAY, 39, @DimPaque), 'Jeudi de l''Ascension'   
            UNION ALL   
        SELECT 3, DATEADD(DAY, 50, @DimPaque), 'Lundi de Pentecôte'  
            UNION ALL 
        SELECT 4, CAST(@an + '0101' AS DATETIME), 'Nouvel an'  
            UNION ALL 
        SELECT 5, CAST(@an + '0501' AS DATETIME), 'Fête du travail'
            UNION ALL   
        SELECT 6, CAST(@an + '0508' AS DATETIME), 'Armistice 39-45'
            UNION ALL
        SELECT 7, CAST(@an + '0815' AS DATETIME), 'Assomption'
            UNION ALL 
        SELECT 8, CAST(@an + '0714' AS DATETIME), 'Fête Nationale'
            UNION ALL 
        SELECT 9, CAST(@an + '1101' AS DATETIME), 'Toussaint'
            UNION ALL 
        SELECT 10, CAST(@an + '1111' AS DATETIME), 'Armistice 14-18'  
            UNION ALL 
        SELECT 11, CAST(@an + '1101' AS DATETIME), 'Noël'
    ) t

    RETURN

END
GO

ALTER FUNCTION FN_DATEDIFF_SELON_HORAIRES_ENTREPRISE2 
(
      @Date1 DATETIME
    , @Date2 DATETIME
)
RETURNS INT AS 
BEGIN

    DECLARE 
          @NB_Jours INT       
        , @Year INT
        , @Jours_Travailles INT
        , @Date1_at_8_am DATETIME
        , @Date2_at_6_pm DATETIME
        , @Excedent INT

    SELECT 
          @NB_Jours = DATEDIFF(DAY, @Date1, @Date2) + 1
        , @Jours_Travailles = 0
        , @Excedent = 0
        , @Date1_at_8_am = DATEADD(HOUR, 8, CAST(FLOOR(CAST(@Date1 AS FLOAT)) AS DATETIME))
        , @Date2_at_6_pm = DATEADD(HOUR, 18, CAST(FLOOR(CAST(@Date1 AS FLOAT)) AS DATETIME))

    DECLARE @emun TABLE (i BIGINT IDENTITY, blank BIT )
    INSERT INTO @emun (blank)
    SELECT NULL
    FROM [master].dbo.spt_values n
    CROSS JOIN (
        SELECT i = 1 
          UNION ALL 
        SELECT 2 
          UNION ALL 
        SELECT 3 
    ) b

    DECLARE @temp TABLE (dt DATETIME)
    INSERT INTO @temp (dt)
    SELECT dt
    FROM (
        SELECT dt = @Date1

        UNION ALL

        SELECT DATEADD(DAY, i, @Date1)
        FROM @emun
        WHERE i < @NB_Jours
    ) d

    DECLARE @temp2 TABLE
    (
          JourId INT NOT NULL
        , JourDate DATETIME NOT NULL
        , JoURLabel VARCHAR(50) NULL
    )

    DECLARE cur CURSOR FAST_FORWARD READ_ONLY LOCAL FOR
        SELECT DISTINCT YEAR(y.dt)
        FROM @temp y

    OPEN cur

    FETCH NEXT FROM cur INTO @Year

    WHILE @@FETCH_STATUS = 0 BEGIN

        INSERT INTO @temp2 (JourId, JourDate, JoURLabel)
        SELECT JourId, JourDate, JoURLabel 
        FROM FN_FERIES_SELON_ANNEE(@Year)

        FETCH NEXT FROM cur INTO @Year

    END

    CLOSE cur
    DEALLOCATE cur

    SELECT @Excedent = @Excedent + 
            CASE WHEN @Date1 > @Date1_at_8_am AND NOT EXISTS(SELECT 1 FROM @temp2 WHERE JourDate = @Date1 OR DATEPART(weekday, @Date1) IN (1,7))
                THEN DATEDIFF(MINUTE, @Date1_at_8_am, @Date1)
                ELSE 0
            END +
            CASE WHEN @Date2 < @Date2_at_6_pm AND NOT EXISTS(SELECT 1 FROM @temp2 WHERE JourDate = @Date2 OR DATEPART(weekday, @Date2) IN (1,7))
                THEN DATEDIFF(MINUTE, @Date2, @Date2_at_6_pm)
                ELSE 0
            END

    SELECT @Jours_Travailles = COUNT(1)
    FROM @temp t
    LEFT JOIN @temp2 t2 ON t.dt = t2.JourDate
    WHERE NOT(t2.JourId IS NOT NULL OR DATEPART(weekday, t.dt) IN (1,7))

    RETURN @Jours_Travailles * 600 - @Excedent

END
于 2013-04-26T10:55:08.363 回答