标量 UDF 与 TVF 的效率

Tho*_* D. 2 sql-server optimization functions set-returning-functions

我正在尝试为我的公司优化汇总代码,但遇到了一个非常奇怪的问题。我将许多标量函数转换为 TVF,它们似乎都比原始函数运行得更快,这很棒。但是,在调用它们的查询中,它们最终的运行速度明显慢于原始查询。这是我的更新的基本概述:

SELECT col1, ..., colx,
    (CASE WHEN x <= 0 OR y <= 0 OR z <= 0 OR z = x 
          THEN output
        WHEN valX <= 0 
          THEN output
        WHEN minimum.min < 1.0 THEN 1.0
        ELSE minimum.min
        END) AS Q,
FROM Tbl1...tblx (series of inner joins)
CROSS APPLY dbo.inlinemin(val1, val2) AS minimum
Run Code Online (Sandbox Code Playgroud)

这是原文的基本轮廓:

SELECT col1, ..., colx,
    (CASE WHEN x <= 0 OR y <= 0 OR z <= 0 OR z = x 
          THEN output
        WHEN valX <= 0 
          THEN output
        ELSE maximum(minimum(val1,val2),1.0)
        END) AS Q,
FROM Tbl1...tblx (series of inner joins)
Run Code Online (Sandbox Code Playgroud)

数字或多或少相同,逻辑也是如此。唯一的区别是我的函数 'inlineMin' 是一个 TVF,而不是原始标量函数的 'maximum' 和 'minimum'。这些函数非常简单,只返回两个传递参数之间的最大值或最小值。甚至执行计划也大致相同。有一次从合并连接到哈希匹配的变化,但是,这种差异的代价很小,无法解释经过时间和 CPU 时间的急剧变化。

当我在汇总查询之外运行函数时,对于大量数据,我的函数比原始函数快。考虑到 TVF 与标量 UDF 的工作方式,这是有道理的。但是,当我在查询中调用它们时,我更新后的版本运行速度大约慢了 6 倍。交叉应用(似乎)不是问题,因为离开交叉应用并简单地使用旧功能

SELECT ...
    ELSE maximum(minimum(val1,val2),1.0)
    END) AS Q,
FROM Tbl1...tblx (series of inner joins)
CROSS APPLY inlinemin(val1, val2) AS NotUsedHere
Run Code Online (Sandbox Code Playgroud)

与原始代码大致一样有效。只有当我在选择中包含我的函数的输出时,查询才会显着变慢。

据我了解,该函数在交叉应用中被调用并运行,这意味着即使它不在选择中,它也应该计算一个值,那么为什么不将它包含在选择中会更快呢?此外,如果上述内容是错误的,为什么我的函数本身会更快,但在查询中使用时运行速度却明显变慢?

编辑:

这是我写的内联 TVF 来替换原来的

CREATE FUNCTION [dbo].[InlineMin](@val1 FLOAT, @val2 FLOAT)
RETURNS TABLE WITH SCHEMABINDING 
AS
RETURN
    SELECT minVal =
    CASE    WHEN @val1 < @val2
            THEN @val1
    ELSE
            ISNULL(@val2,@val1)
END
Run Code Online (Sandbox Code Playgroud)

这是我重写的匿名查询计划:https : //www.brentozar.com/pastetheplan/? id =ryKR_Q0Em

以及原始的匿名查询计划:https ://www.brentozar.com/pastetheplan/?id=SJsS1-A4X

Pau*_*ite 5

据我了解,该函数在交叉应用中被调用并运行,这意味着即使它不在选择中,它也应该计算一个值,那么为什么不将它包含在选择中会更快呢?

优化器非常擅长删除计算最终结果(顶级投影)中不需要的表达式的子树。当您从选择列表中删除该值时,根本没有完成计算该值所需的工作。

此外,如果上述内容是错误的,为什么我的函数本身会更快,但在查询中使用时运行速度却明显变慢?

这很难从匿名计划中详细评估。然而,删除标量 T-SQL 函数允许优化器考虑并行计划。您可能希望使用OPTION (MAXDOP 1)查询提示来测试您重写的查询,以查看所选串行计划与原始计划的比较情况。

并行计划并不总是更好(尽管只有在优化器的成本似乎较低时才会选择它们)。您的案例成本相对较低,因此优化器认为不值得探索大量替代方案。在某些情况下,考虑串行和并行计划所花费的时间会对最终计划质量产生反作用。

如果这有点含糊,我深表歉意,但匿名计划确实很难具体说明。在所有条件相同的情况下,内联函数目前将优于标量函数。可悲的是,所有事物很少是平等的。