使用函数从TSQL SELECT中删除周末和银行假日(英国)

时间:2013-08-01 09:41:03

标签: sql-server tsql

我有一个TSQL函数,我认为(我不熟悉SQL语法)会在调用时删除周末:

ALTER FUNCTION dbo.fn_WorkDays (@StartDate AS DATETIME, @EndDate AS DATETIME)
--Define the output data type.
RETURNS INT
AS
--Calculate the RETURN of the function.
BEGIN
    RETURN (
     SELECT
        (DATEDIFF(dd,@StartDate, @EndDate)+1)--Start with total number of days including weekends +1 Includes the day run
        -(DATEDIFF(wk,@StartDate, @EndDate)*2)--Subtact 2 days for each full weekend
        -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' --If StartDate is a Sunday, Subtract 1
            THEN 1 
            ELSE 0 
        END) 
        -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday'--If EndDate is a Saturday, Subtract 1 
            THEN 1 
            ELSE 0 
        END))       
END

但我还想带走任何现有的银行假期,我可以使用类似的代码从表中获取:

  SELECT COUNT([Date])
  FROM [InvoiceManagement].[dbo].[tblBankHolidays]
  WHERE [Date] BETWEEN '2006-04-14' AND '2006-05-29'--eventually replace dates with @StartDate, @EndDate 

是否可以将上述选择拼接到函数中,以便在返回INT之前从结果中减去任何现有的银行?如果是这样,我将非常感谢我如何在TSQL中表现不强

2 个答案:

答案 0 :(得分:3)

SQL Server 2008:

首先,我会使用一张桌子作假期,我会插入所有假期(#Saturdays& Sundays)

CREATE TABLE dbo.Holiday(HolidayDate DATE PRIMARY KEY);
GO
INSERT INTO dbo.Holiday(HolidayDate) VALUES ('2013-07-06'); -- Saturday
GO
INSERT INTO dbo.Holiday(HolidayDate) VALUES ('2013-07-07'); -- Sunday
GO
...

然后,为了获得两个日期之间的工作日,我会使用此查询:

DECLARE @StartDate DATE,@EndDate DATE;
SELECT  @StartDate='2013-07-01',
    @EndDate='2013-07-31';

SELECT  DATEDIFF(DAY,@StartDate,@EndDate) + 1 - COUNT(*) AS WorkingDaysCount
FROM    dbo.Holyday h
WHERE   h.HolidayDate BETWEEN @StartDate AND @EndDate;

注意:DATENAME不具有确定性:

SET LANGUAGE english;
SELECT DATENAME(dw, '2013-08-01') AS DateNm_EN;
GO
SET LANGUAGE romanian;
SELECT DATENAME(dw, '2013-08-01') AS DateNm_RO;
GO

结果:

DateNm_EN
---------
Thursday

DateNm_RO
---------
joi

修改1:

USE [InvoiceManagement];
GO
CREATE FUNCTION dbo.fn_WorkDays_v2 (@StartDate AS DATE, @EndDate AS DATE) -- Arguments should have the same type as column's type
RETURNS INT
AS
BEGIN
    DECLARE @HolidaysCount INT;

    SELECT @HolidaysCount=COUNT(*)
    FROM [dbo].[tblBankHolidays] h
    WHERE h.[Date] BETWEEN @StartDate AND @EndDate;

    DECLARE @WeekendDaysCount INT;
    WITH N10(Num)
    AS
    (
        SELECT Num FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))  n(Num)
    ), N100(Num)
    AS
    (
        SELECT (a.Num-1)*10 + b.Num AS Num
        FROM N10 a CROSS JOIN N10 b
    ), N10000(Num)
    AS
    (
        SELECT (a.Num-1)*100 + b.Num AS Num
        FROM N100 a CROSS JOIN N100 b
    )
    SELECT @WeekendDaysCount=COUNT(*)
    FROM N10000 n
    WHERE DATEDIFF(DAY,@StartDate,@EndDate) >= n.Num
    AND DATEDIFF(DAY,0, DATEADD(DAY,n.Num-1,@StartDate)) % 7 IN (5,6); -- 5=Saturday, 6=Sunday

    RETURN (DATEDIFF(DAY,@StartDate, @EndDate)+1 - @HolidaysCount - @WeekendDaysCount);       
END

答案 1 :(得分:0)

我相信我找到了另一种解决方案;一个比上面更简单的解决方案(对我而言);我认为以上内容对我来说有点难以阅读并理解对不起@Bogdan。 这是使用该功能的脚本,当我在日历上检查它们时,似乎显示正确的结果:

SELECT 
cir.[PW Number] AS PWNum
--Get the correct number of working dayts since the order date by using the fn_WorkDays function
,CONVERT(VARCHAR(10),singleended2.dbo.fn_WorkDays(cir.[Install Date], GETDATE()),103) AS NumberOfDaysSinceOrderDate 
,CONVERT(VARCHAR(10), ISNULL(cir.[Install Date],'01/01/1900'),103) AS  OrderDate--Get the order dates
,ISNULL(cirRep.CurrentStage, 'Not Set') AS CurrentStage
,cir.[ID] as CircuitID
FROM Quotebase.dbo.Circuits cir
    LEFT JOIN Quotebase.dbo.CircuitReports cirRep ON Cir.[PW Number] = CirRep.PWNumber
WHERE Cir.Status='New Circuit Order' 
ORDER BY Cir.[PW Number]

以下是名为的函数脚本:

ALTER FUNCTION dbo.fn_WorkDays (@StartDate AS DATETIME, @EndDate AS DATETIME)
--Define the output data type.
RETURNS INT
AS
--Calculate the RETURN of the function.
BEGIN
    RETURN (
     SELECT
        (DATEDIFF(dd,@StartDate, @EndDate)+1)--Start with total number of days including weekends +1 Includes the day run
        -(DATEDIFF(wk,@StartDate, @EndDate)*2)--Subtact 2 days for each full weekend
        -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' --If StartDate is a Sunday, Subtract 1
            THEN 1 
            ELSE 0 
        END) 
        -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday'--If EndDate is a Saturday, Subtract 1 
            THEN 1 
            ELSE 0 
        END)
        --Now check if there are any bank holidays between the start and end and remove that amount 
        - (SELECT COUNT(ivm.[Date])
          FROM [EUROPEVUK386].[InvoiceManagement].[dbo].[tblBankHolidays] ivm
          WHERE ivm.[Date] BETWEEN @StartDate AND @EndDate)     
          --WHERE ivm.[Date] BETWEEN '2006-04-14' AND '2006-05-29')     
)
 END
GO

我可能会去检查代码并编辑它,如果确实不正确的话。