为什么在 WHERE 子句中使用函数时会有显着的性能差异

问题描述 投票:0回答:2

我在不同的存储过程中使用了 where 子句的相同部分,我试图消除重复并确保代码不会不同步。所以我决定将这个检查提取到一个函数中。

我知道通常这不是一个好的做法,因为这会阻止 SQL Server 有效地使用索引查找,但是在我的场景中,两端都有通配符搜索,无论如何都会导致索引扫描,所以我没想到会有如此巨大的性能区别。

原始查询:

SELECT COUNT(1) 
FROM MyTable 
WHERE MyField LIKE '%term1%' 
   OR MyField LIKE '%term2%' 
   OR MyField LIKE '%term3%'

功能:

CREATE FUNCTION dbo.fn_MyFunc
    (@CodeResult VARCHAR(MAX))
RETURNS BIT
WITH SCHEMABINDING, RETURNS NULL ON NULL INPUT  
AS
BEGIN
    RETURN 
        CASE 
            WHEN @CodeResult LIKE '%term1%' 
                 OR @CodeResult LIKE '%term2%' 
                 OR @CodeResult LIKE '%term3%' 
                THEN 1
                ELSE 0
        END;
END;

更新查询:

SELECT COUNT(1) 
FROM MyTable 
WHERE dbo.fn_MyFunc(MyField) = 1

查询执行计划:

enter image description here

我错过了什么吗?为什么相差近 10 倍?

sql-server function performance where-clause
2个回答
0
投票

我的猜测是,您为表中的每一行调用该函数,并且每次调用它时都会分配一个新的 4K VARCHAR,从而绕过数据库引擎能够围绕 LIKE 语句进行的调整和优化。 相反,您将取消该优化以及该列上的任何索引。


0
投票

明显的区别是一个计划是并行执行的,另一个计划是串行执行的。非内联标量 UDF 会阻止并行计划。因此,在这种情况下,它们都同样效率低下,因为它们都有完整的索引扫描,但并行计划中的运行时间较短,因为它有多个工作线程并行处理(在您的情况下,DOP 可能为 8)

如果您想使其可内联,但在模块中并且不在可用内联标量 UDF 的版本/兼容级别上,则可以使用内联 TVF。

一个示例方法是

CREATE FUNCTION dbo.fn_MyFilterFunc 
(@CodeResult VARCHAR(MAX))
RETURNS TABLE
AS
    RETURN
      SELECT 1 AS Result
      WHERE  @CodeResult LIKE '%term1%'
              OR @CodeResult LIKE '%term2%'
              OR @CodeResult LIKE '%term3%' 

然后您可以将其用作

SELECT COUNT(1) 
FROM MyTable 
CROSS APPLY dbo.fn_MyFilterFunc(MyField)
© www.soinside.com 2019 - 2024. All rights reserved.