【问题标题】:Are UDFs in T-SQL safe to use now?现在可以安全使用 T-SQL 中的 UDF 吗?
【发布时间】:2019-10-15 22:31:58
【问题描述】:

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

但是,当我准备做 show-n-tell 时,运行第一个块和第二个块所花费的时间几乎完全相同,大约 8 秒。我记得运行过一个非常相似的实验大约 6 年前,并注意到执行时间呈指数级差异。

UDF 现在可以安全使用了吗?有什么变化?

【问题讨论】:

  • UDF 并不邪恶。像所有代码一样,它们可能写得不好或以不太理想的方式编写。例如,当您的 UDF 可以简化为一个时,它有多个语句 >>> RETURN @Numerator/NullIf(@Denominator,0)
  • 像所有工具一样,它们各有利弊。但就像所有 SQL 一样,性能完全取决于情境。在where 子句中避免它们可能是一个有效的规则,但是您是否想在多个位置重复您的 SQL 并冒忘记更新其中一个的风险。在许多情况下,可以将 UDF 替换为 ITVF,它没有典型的 UDF 不良性能,因为它像视图一样插入内联。
  • 我知道只做一个直接的“返回”的单一声明,因为这是我的第一个方法。当它快速返回时,我夸大了它以模仿大多数其他最基本的 UDF。它仍然很快返回,因此,我决定发布更复杂的版本,以进一步证明简单 UDF 的性能已显着提高(或者,......也许我的记忆力真的很差)。
  • 我确信 SQL Server 的每个部分在 14 年后都会表现得更好:)
  • @AlanBurstein 我的主张成立。每个工具在正确使用时都有价值。首先,我怀疑大多数理性的人永远不会使用 UDF 作为挑战,如果他们这样做了,他们很快就会意识到这是一个愚蠢的想法。其次,性能很重要,但不是一切。还应考虑数据完整性、可管理性、可用性……。例如,UDF 可以通过减少接触点来提高一致性。一位客户添加了风险评级。他们花费了数千个工时来识别、调整和验证无数的遗留报告和流程。我有一个接触点。

标签: sql-server tsql user-defined-functions sql-execution-plan


【解决方案1】:

如果“安全”是指“不慢和/或低效”,那么答案是: 内联 = 好,而不是内联 = 坏。* 不要拿我的工作为代价,问问微软吧:

https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/scalar-udf-inlining?view=sql-server-ver15#performance-of-scalar-udfs

T-SQL 内联函数可能写得不好,但 T-SQL 标量 UDF 和多语句表值函数 (mTVF) 保证会破坏性能。

这里有几个链接,我将一些测试放在一起,或者更详细地讨论这个问题。

When would you use a table-valued function?

https://www.sqlservercentral.com/forums/topic/inline-vs-multi-statement-table-valued-function-temp-tables-vs-ctes#post-1784973

现在,SQL Server 2019 引入了内联标量和 mTVF。这些比他们的前辈更好,但并不完美。例如,使用标量 UDF 作为 CHECK 约束或计算列将强制优化器选择带有引用该表的查询的串行计划(插入/更新/删除和选择)。即使没有在查询中调用引用该函数的列也是如此。在 SQL 2019 中仍然如此。

【讨论】:

  • 我记得在某处读到内联函数具有更好的性能,因为查询优化器可以使用它们,就好像它们是使用它们的代码的一部分一样,这意味着优化过程是在整个代码上完成的实际运行,但非内联函数并非如此。我喜欢奥威尔式的参考,但这不是我投赞成票的唯一原因。
猜你喜欢
  • 1970-01-01
  • 2017-07-30
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 2018-03-20
  • 2010-10-11
  • 2013-01-06
相关资源
最近更新 更多