这是 SQL Server 中标量 UDF 的一个众所周知的问题。
它们没有内联到计划中,与具有相同的内联逻辑相比,调用它们会增加开销。
以下在我的机器上只需要不到 2 秒的时间
WITH T10(N) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
) --10 rows
, T(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM T10 a, T10 b, T10 c, T10 d, T10 e, T10 f, T10 g) -- 10 million rows
SELECT MAX(N - N)
FROM T
OPTION (MAXDOP 1)
创建简单的标量 UDF
CREATE FUNCTION dbo.F1 (@N BIGINT)
RETURNS BIGINT
WITH SCHEMABINDING
AS
BEGIN
RETURN (@N - @N)
END
将查询更改为MAX(dbo.F1(N)) 而不是MAX(N - N),STATISTICS TIME OFF 需要大约 26 秒,而启用它需要 37 秒。
1000 万次函数调用平均增加 2.6μs / 3.7μs。
运行 Visual Studio 分析器显示绝大多数时间都在 UDFInvoke 下进行。调用堆栈中的方法名称给出了额外开销在做什么(复制参数、执行语句、设置安全上下文)的一些概念。
将逻辑移入内联表值函数
CREATE FUNCTION dbo.F2 (@N BIGINT)
RETURNS TABLE
RETURN(SELECT @N - @N AS X)
并将查询重写为
SELECT MAX(X)
FROM Nums
CROSS APPLY dbo.F2(N)
执行速度与不使用任何函数的原始查询一样快。