性能函数SQL Server
我正在创建一个函数来返回两个日期之间的工作分钟数 这会返回精确的分钟数,但当我在许多记录上使用它时,处理时间非常长 我有3个功能:性能函数SQL Server,sql,sql-server,function,sql-server-2000,Sql,Sql Server,Function,Sql Server 2000,我正在创建一个函数来返回两个日期之间的工作分钟数 这会返回精确的分钟数,但当我在许多记录上使用它时,处理时间非常长 我有3个功能: CREATE FUNCTION FN_FERIES_SELON_ANNEE (@YEAR INT) RETURNS @FERIES TABLE (JourId INT NOT NULL, JourDate DATETIME NOT NULL, JoURLabel VAR
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
-----------------------------------------------------------------------
-----------------------------------------------------------------------
-----------------------------------------------------------------------
我计算每个期间的每一天,生成该年的公共假日表
最好的解决方案是检查我是否还没有创建此表,是否在其中搜索,如果没有,则创建并存储它
但是我不知道怎么做
我需要在上级范围内创建一个表,并将其作为参数传递到我的函数中。表类型参数如何:
create type FERIES as table (
JourId INT NOT NULL,
JourDate DATETIME NOT NULL,
JoURLabel VARCHAR(50) NULL
)
GO
在外部作用域中,使用FN_FERIES_SELON_ANNEE函数填充表值:
declare @var FERIES;
insert into @var
select * from FN_FERIES_SELON_ANNEE(@YEAR)
然后将其传递给其他函数使用
你的另一个职能是:
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
尝试使用XML-
DECLARE @XML XML
SELECT @XML = (
SELECT JourId, JourDate, JoURLabel
FROM @JoursFeries t
FOR XML AUTO
)
RETURN @XML
更新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
2000年更新2:
如果使用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
你为什么不为公众假期创建一个永久表呢?这是我最后决定做的,谢谢。我不得不说我不太明白你的所有解决方案,我是一个初学者,所以我只是尝试粘贴它,我有以下错误:Msg 170,Niveau 15,État 1,Procédure FN_FERIES_SELON ANNEE,Ligne 54第54行:“XML”附近的语法不正确。Msg 170,Niveau 15,État 1,程序FN_JOUR_faville,Ligne 11第11行:“.”附近的语法不正确。Msg 156,Niveau 15,État 1,过程FN_DATEDIFF_SELON_HORAIRES_enterprise2,对齐关键字“WITH”附近的36个不正确语法。Msg 170,Niveau 15,État 1,过程FN_DATEDIFF_SELON_HORAIRES_enterprise2,Ligne 55第55行:“MAXRECURSION”附近的语法不正确。您的SQL Server版本是什么?我在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
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