SQL 服务器上的工作日在函数中使用 CTE

Working days on SQL Server using CTE in a function

我正在尝试编写一个函数来 return 两个日期之间的工作日数。我有一个包含所有银行假日的 table,其中有两个字段:BHID 和 BHDate。

我编写了以下 SELECT 查询来检验我的理论,它运行良好:

DECLARE @StartDate AS Date, @EndDate AS Date

SET @StartDate = '2015-01-01'
SET @EndDate = '2015-07-01'

;WITH CTE AS (
    SELECT @StartDate AS StartDate, @EndDate AS EndDate, @StartDate AS DateCalc
    UNION ALL
    SELECT @StartDate AS StartDate, @EndDate AS EndDate, DATEADD(dd, 1, DateCalc) AS DateCalc FROM CTE
    WHERE DATEADD(dd, 1, DateCalc) <= EndDate)

SELECT COUNT(*) AS Days
FROM CTE
LEFT JOIN psd.dbo.LUBankHolidays BH
ON CTE.DateCalc = BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, DateCalc) ,1) <> 'S'
option (Maxrecursion 0);

但是当我尝试将它放入一个函数中时,我在 SET 行周围遇到了一些错误,这可能是我在函数方面不如我的其他知识那么好,或者我想知道它是否在 SET 行之前没有使用 CTE 怎么办?这是我的尝试:

CREATE FUNCTION [dbo].[WorkingDays]
(@StartDate AS Date, @EndDate AS Date
)
RETURNS INT
AS
BEGIN

DECLARE @Days AS INT

;WITH CTE AS (
    SELECT @StartDate AS StartDate, @EndDate AS EndDate, @StartDate AS DateCalc
    UNION ALL
    SELECT @StartDate AS StartDate, @EndDate AS EndDate, DATEADD(dd, 1, DateCalc) AS DateCalc FROM CTE
    WHERE DATEADD(dd, 1, DateCalc) <= EndDate)

SET @Days = 
(SELECT COUNT(*) AS Days
FROM CTE
LEFT JOIN psd.dbo.LUBankHolidays BH
ON CTE.DateCalc = BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, DateCalc) ,1) <> 'S'
option (Maxrecursion 0);)

RETURN @Days

END

我什至尝试将 INT 的结果插入到临时 table 中,然后在 returning @Days 之前设置 @Days 之后删除它,但这会导致错误允许作为函数的一部分生成临时 tables。

任何帮助都会很棒,我确定这是一件小事,但目前只是在逃避我。

使用这个功能

    CREATE FUNCTION [dbo].[WorkingDays]
    (@Start_Date AS Date, @end_Date AS Date
    )
    RETURNS INT
    AS
    BEGIN

    DECLARE @Days AS INT

        WITH    AllDays
                  AS ( SELECT   @start_date AS [Date], 1 AS [level]
                       UNION ALL
                       SELECT   DATEADD(DAY, 1, [Date]), [level] + 1
                       FROM     AllDays
                       WHERE    [Date] < @end_date )

   SELECT @Days =COUNT(*) AS Days
    FROM AllDays
    LEFT JOIN psd.dbo.LUBankHolidays BH
    ON AllDays.Date= BH.BHDate
    WHERE BHID IS NULL
    AND LEFT(DATENAME(dw, AllDays.Date) ,1) <> 'S'

    RETURN @Days

    END

因为 CTE 只能在查询中使用,而不是使用 SET 来设置变量,所以使用 SELECT 代替:

SELECT @Days = 
(SELECT COUNT(*) AS Days
FROM CTE …

来自MSDN

A CTE must be followed by a single SELECT, INSERT, UPDATE, or DELETE statement that references some or all the CTE columns.

stuartd 的帮助下解决了这个问题:

CREATE FUNCTION [dbo].[WorkingDays]
(@StartDate AS Date, @EndDate AS Date
)
RETURNS INT
AS
BEGIN

DECLARE @Days AS INT

;WITH CTE AS (
    SELECT @StartDate AS StartDate, @EndDate AS EndDate, @StartDate AS DateCalc
    UNION ALL
    SELECT @StartDate AS StartDate, @EndDate AS EndDate, DATEADD(dd, 1, DateCalc) AS DateCalc FROM CTE
    WHERE DATEADD(dd, 1, DateCalc) <= EndDate)

SELECT @Days = 
(SELECT COUNT(*) AS Days
FROM CTE
LEFT JOIN psd.dbo.LUBankHolidays BH
ON CTE.DateCalc = BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, DateCalc) ,1) <> 'S')
option (Maxrecursion 0);

RETURN @Days

END

谢谢大家