[14年以来,我一直以宣誓的引用为生:“永远不要在受影响的一两行以上使用UDF。”
我将通过一个非常基本的示例来与我的团队分享UDF的糟糕程度:
/*
CREATE FUNCTION dbo.Test (@Numerator Float, @Denominator Float)
RETURNS Float AS
BEGIN
DECLARE @Return Float;
SET @Return = @Numerator / NullIf(@Denominator,0);
RETURN @Return
END
GO
*/
----------------------------------------------------------------------
SELECT rn / NullIf(3.00,0)
FROM
(
SELECT TOP 1000000 rn = Convert(Float,ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
ORDER BY s1.[object_id]
) tbl;
----------------------------------------------------------------------
SELECT dbo.Test (rn,3.00)
FROM
(
SELECT TOP 1000000 rn = Convert(Float,ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
ORDER BY s1.[object_id]
) tbl;
----------------------------------------------------------------------
--DROP FUNCTION dbo.Test;
GO
但是,当我准备进行表演时,运行第一个程序块和第二个程序块所需的时间几乎完全相同,大约8秒。我记得进行了一个非常类似的实验, 6年前,并且注意到执行时间成指数增长。
UDF现在可以安全使用了吗?发生了什么变化?
如果用“安全”来表示“不是慢和/或低效”,那么答案是:Inline =好,而不是inline =不好。*不要为此而工作,只需询问Microsoft:
T-SQL内联函数can写得不好时会很慢,但是T-SQL标量UDF和多语句表值函数(mTVF)肯定会破坏性能。
这里有几个链接,我在其中进行了一些测试,或者对此进行了更详细的讨论。
When would you use a table-valued function?
现在,SQL Server 2019引入了内联标量和mTVF。这些比他们的前辈更好,但并不完美。例如,将标量UDF用作CHECK约束或计算列将迫使优化器选择带有引用该表的查询的串行计划(插入/更新/删除和选择)。即使在查询中未调用引用该函数的列也是如此。在SQL 2019中仍然如此。