将SQL Server UDF转换为内联表值函数

时间:2017-09-06 15:10:26

标签: sql sql-server

我是新手,也是SQL新手。我得到了这个技巧,创建了一个标量函数,扩展了内置DateAdd函数的功能(即排除周末和假日)。它适用于单个日期,但是当我在桌面上使用它时,它非常慢。

我已经看到一些建议使用内联表值函数。有人会如此友善地指出我的方向,我将如何将下面的内容转换为内联表值函数?我非常感激。

ALTER FUNCTION [dbo].[CalcWorkDaysAddDays]
    (@StartDate AS DATETIME, @Days AS INT) 
RETURNS DATE 
AS 
BEGIN 
    DECLARE @Count INT = 0 
    DECLARE @WorkDay INT = 0 
    DECLARE @Date DATE = @StartDate 

    WHILE @WorkDay < @Days 
    BEGIN 
        SET @Count = @Count - 1 
        SET @Date = DATEADD(DAY, @Count, @StartDate) 

        IF NOT (DATEPART(WEEKDAY, @Date) IN (1,7) OR 
               EXISTS (SELECT * FROM RRCP_Calendar WHERE Is_Holiday = 1 AND Calendar_Date = @Date)) 
        BEGIN 
            SET @WorkDay = @WorkDay + 1 
        END 
    END 

    RETURN @Date 
END 

3 个答案:

答案 0 :(得分:1)

这应该可以解决问题......

CREATE FUNCTION dbo.tfn_CalcWorkDaysAddDays 
(
    @StartDate DATETIME,
    @Days INT
)
RETURNS TABLE WITH SCHEMABINDING AS 
RETURN
    SELECT 
        TheDate = MIN(x.Calendar_Date)
    FROM (  
            SELECT TOP (@Days)
                c.Calendar_Date
            FROM 
                dbo.RRCP_Calendar c
            WHERE 
                c.Calendar_Date < @StartDate
                AND c.Is_Holiday = 0
                AND c.is_Weekday = 1    -- this should be part of your calendar table. do not calculate on the fly.
            ORDER BY
                c.Calendar_Date DESC
            ) x;
GO

注意:为了获得最佳效果,您需要在日历表上添加唯一的,已过滤的非聚集索引...

CREATE UNIQUE NONCLUSTERED INDEX uix_RRCPCalendar_CalendarDate_IsHoliday_isWeekday ON dbo.RRCP_Calendar (
    Calendar_Date, Is_Holiday, is_Weekday)
WHERE Is_Holiday = 0 AND is_Weekday = 1;

答案 1 :(得分:0)

  

说[dbo]。[CalcWorkDaysAddDays],getdate(),2将返回9月8日,   2017年,因为它增加了两天。此功能类似于DateAdd   但它不包括周末和假日

您发布的代码不会这样做。 但是如果你想要描述的结果,那么函数可以像这样:

(img)

答案 2 :(得分:0)

试试这个,看它是否返回与你的函数相同的值,只是没有循环:

SELECT WorkDays =
    DATEADD(WEEKDAY, @Days, @StartDate) -
    (SELECT COUNT(*)
    FROM RRCP_Calendar
    WHERE Is_Holiday = 1
    AND Calendar_Date >= @StartDate
    AND Calendar_Date <= DATEADD(DAY, @Days, @StartDate)
    )

是的,使用非过程表值函数有时可以获得更好的性能,但是你必须正确设置它。查找SARGability和非过程表值函数以获取更多信息,但如果上述查询有效,则应该可以解决这个问题:

CREATE FUNCTION dbo.SelectWorkDaysAddDays(@StartDate DATE, @Days INT)
RETURNS TABLE
AS

RETURN
    SELECT WorkDays =
        DATEADD(WEEKDAY, @Days, @StartDate) -
        (SELECT COUNT(*)
        FROM RRCP_Calendar
        WHERE Is_Holiday = 1
        AND Calendar_Date >= @StartDate
        AND Calendar_Date <= DATEADD(DAY, @Days, @StartDate)
        )
GO

然后使用OUTER APPLY join:

调用该函数
SELECT y.foo
    , y.bar
    , dt.WorkDays
FROM dbo.YourTable y
    OUTER APPLY dbo.SelectWorkDaysAddDays(@StartDate, @Days) dt