【问题标题】:SQL Server : use function to calculate a new columnSQL Server:使用函数计算新列
【发布时间】:2019-05-21 04:41:24
【问题描述】:

我创建了一个函数,该函数使用日历表计算两个日期之间的工作日数,以排除节假日和周末。

 CREATE FUNCTION dbo.BusinessDays 
     (@sDate SMALLDATETIME, 
      @eDate SMALLDATETIME) 
 RETURNS TABLE 
 AS 
     RETURN (SELECT COUNT(*) dt 
             FROM dbo.Calendar 
             WHERE dt BETWEEN @sDate AND @eDate) 
Go

我的问题是:如何使用此函数计算另一个表 (dbo.HolidayRequestForm) 中的列,该表有两个日期列?

我试过了

ALTER TABLE Test
    ADD HoursTaken AS dbo.BusinessDays ([StartDate], [FinishDate])

【问题讨论】:

    标签: sql sql-server tsql


    【解决方案1】:

    我认为最好的办法是重构你的函数以返回一个标量值:

    CREATE FUNCTION dbo.BusinessDays
    (
        @sDate SMALLDATETIME,
        @eDate SMALLDATETIME
    )
    RETURNS INT
    AS
    BEGIN
        DECLARE @RTN INT;
    
        SELECT @RTN = COUNT(*) 
        FROM dbo.Calendar
        WHERE dt BETWEEN @sDate AND @eDate;
    
        RETURN @RTN;
    END
    

    那么你添加计算列的 SQL 保持不变。

    如果您的函数需要保留为表值函数,则需要使用CROSS APPLY

    SELECT * 
    FROM dbo.HolidayRequestForm
        CROSS APPLY dbo.BusinessDays(StartDate, FinishDate) d
    

    如您所见,这会使使用计算列变得非常棘手。

    几个不相关的点:

    • 我不确定HoursTaken 是否是计算列的最佳名称,因为它似乎存储了两个日期之间的天数(不是小时数)。
    • 在查询中使用日期范围时,建议使用WHERE DateColumn >= @DateStart AND DateColumn < @EndDate。如需更多信息,请参阅this blog post

    【讨论】:

    • @GordonLinoff:我不知道 SQL Server 支持内联标量函数(至少是从表中提取数据的函数)。我在哪里可以找到语法/示例?
    • @zack - SQL Server 不支持内联标量函数(除了 SQL 2019 -YAY!)。由于多种原因,T-SQL 内联表值函数性能更好。由于您不能对计算列使用内联表值函数,因此 OP 可以只使用内联 SQL;它将实现目标而不会出现疯狂的性能损失。
    • @Zack。 . .那是某种形式的一厢情愿。
    • @conor8630 - 我想是的。还没有机会玩 2019 年
    【解决方案2】:

    您想使用内联(纯)T-SQL 函数是对的。 SQL Server 中有两种 T-sql 函数:Inline Table Valued functions 和 Wrong。不幸的是,您不能对计算列使用内联表值函数,如果性能非常重要,您不要希望对计算列使用 T-SQL 标量 UDF。

    好消息是,您不需要这样做。你有几个选择。首先,您不需要日历表来计算工作日,您可以将此逻辑用于计算列:

    CASE WHEN SIGN(DATEDIFF(dd, @startDate, @endDate)) > -1 THEN
                    (DATEDIFF(dd, @startDate, @endDate) + 1) --total days including weekends
                   -(DATEDIFF(wk, @startDate, @endDate) * 2) --Subtact 2 days for each full weekend
        -- Subtract 1 when startDate is Sunday and Substract 1 when endDate is Sunday: 
        -(CASE WHEN DATENAME(dw, @startDate) = 'Sunday'   THEN 1 ELSE 0 END)
        -(CASE WHEN DATENAME(dw, @endDate)   = 'Saturday' THEN 1 ELSE 0 END)
    

    您还可以使用此逻辑来创建更灵活的索引视图,可能是更好的选择。

    这里有几个很好的链接,说明为什么您从不使用 T-SQL 标量 UDF 作为计算列或约束:

    A Computed Column with a (scalar udf) might Impact Query Performance——程坤(SQLCAT)

    Another Hidden Parallelism Killer: Scalar UDFs In Check Constraints – Erik Darling(本文也适用于计算列)

    Another reason why scalar functions in computed columns is a bad idea – 埃里克·达林

    Be careful with constraints calling UDFs – Tibor Karaszi

    Beware-row-row-operations-udf-clothing – 布赖恩·莫兰

    Why does the Execution Plan include a scalar udf call for a persisted computed column? – 堆栈溢出

    【讨论】:

    • 在查找两个日期之间的差异时,我还需要删除假期,因此日历表最适合此目的。
    猜你喜欢
    • 1970-01-01
    • 2011-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-29
    • 2016-05-05
    • 1970-01-01
    相关资源
    最近更新 更多