SQLServer:为啥要避免使用表值用户定义函数?

Posted

技术标签:

【中文标题】SQLServer:为啥要避免使用表值用户定义函数?【英文标题】:SQLServer: Why avoid Table-Valued User Defined Functions?SQLServer:为什么要避免使用表值用户定义函数? 【发布时间】:2009-07-03 22:44:32 【问题描述】:

我有一个相当大的查询,需要在几个存储过程中,我想将其转换为 UDF 以使其更易于维护(视图不起作用,这需要一堆参数) ,但是我与之交谈过的每个人都告诉我,UDF 非常慢。

虽然我不知道究竟是什么让它们变慢,但我会猜测它们是,但鉴于我没有在连接中使用这个 UDF,而是返回一个表变量,我认为它不会那么糟糕。

所以我想问题是,我应该不惜一切代价避免 UDF 吗?谁能指出具体的证据表明它们速度较慢?

【问题讨论】:

您能否发布您所询问的部分或全部 UDF?是内联表 UDF 还是多行? 多行UDF,但是不能贴出代码。 UDF 似乎受到了不好的评价。我们将表值 UDF 广泛用于针对大型数据集的一些复杂计算。表现非常出色。我们的许多 UDF 也是多行的,在我们的案例中似乎不会对性能产生重大影响。 【参考方案1】:

标量 UDF 非常慢,内联 UDF 实际上是宏,因此它们非常快: 几篇文章:

Reuse Your Code with Table-Valued UDFs

Many nested inline UDFs are very fast

有关标量 UDF 速度缓慢的更多链接:

SQL Server Performance patterns of a UDF with datetime parameters

Not all UDFs are bad for performance

【讨论】:

由于查询的复杂性,我使用的是多语句返回 UDF,而不是单个表选择...您认为这仍然很快吗? 根据我的经验,嵌套的内联 UDF 可以很好地降低复杂性。多语句的通常(并不总是)有点慢。只有基准测试才能显示在您的特定情况下有多慢。我会先尝试嵌套的内联 UDF。 @AlexKuznetsov:您能否发布一个链接来解释标量 UDF 如何以及为何缓慢? @John Saunders:已添加链接。第一,第三,第四个链接解释它。 @AlexKuznetsov:仍然不确定。您能否检查一下我添加到答案中的“大评论”,如果您觉得这两个 SELECT 之间有区别,请告诉我?【参考方案2】:

正如您所指出的,(表)udf 的结果不会与任何东西相结合,那么应该不会对性能产生任何影响。

为了解释一下为什么 UDF 会被认为是缓慢的(实际上只是以错误的方式使用),请考虑以下示例;

我们有表 A 和表 B。假设我们有一个类似的连接

选择 A.col1, A.col2, B.Col随便 从 一种 在 A.aid = b.fk_aid 上加入 B 在哪里 B.someCol = @param1 AND A.anotherCol = @param2

在这种情况下,SQL Server 将尽最大努力以它知道的最高效的方式返回结果。其中一个主要因素是减少磁盘读取。所以 - 它将使用 JOIN 和 where 子句中的条件来评估(希望使用索引)返回多少行。

现在 - 假设我们提取了一些用于限制返回到 UDF 的数据量的条件。现在 - 查询优化器不能再从磁盘拉回最小数量的行,它只能处理它提供的条件。简而言之 - 始终评估表 udf 并在返回到主 sproc 之前返回数据,因此,如果原始连接中有一些其他条件可能导致更少的磁盘读取 - 这将仅适用于数据在被拉入存储过程之后。

假设我们创建了一个 UDF 来从表 B 中选择与 where 子句匹配的行。如果表 B 中有 100k 行并且其中 50% 符合 where 子句的条件 - 那么所有这些行都将返回到存储过程以与表 A 进行比较。现在如果现在只有 10% 的行在表 A 中有匹配项我们只讨论了我们想要处理的表 B 的 5%,但我们已经撤回了 50%,其中大部分是我们不想要的!

如果这完全是胡言乱语的道歉 - 请告诉我!

【讨论】:

好的,所以你说不要在 JOIN 的 WHERE 子句中使用标量 UDF。还有其他情况会减慢速度吗?另外,准确地说,在这种情况下,您似乎不是在说 UDF 本身很慢,而是在这种情况下,它阻止了优化器进行优化,从而减慢了速度。【参考方案3】:

你能发布你的代码吗?一般来说,如果您在查询的 select 子句中使用标量 udf,则 udf 中的语句将在查询返回的每一行中执行一次。最好对一个值为 udf 的表执行连接,或者找到某种方法在主 SQL 语句中使用连接来执行 udf 中的逻辑。

【讨论】:

我没有在连接中使用它。它是一个返回表值的 UDF。【参考方案4】:

您是否出于某种原因不想使用 stored procedure 而不是 UDF?

【讨论】:

主要是因为你不能在SQLServer中这样做,你必须将一个sproc的返回插入到一个临时表中。 对不起,在 SQL Server 中不能做什么?一个存储过程可以调用另一个存储过程。 不能像在 FireBird 中那样做 SELECT * FROM EXEC Sproc。您必须改为创建一个临时表,然后选择其中。我想避免这种情况。 但是,您不需要执行 SELECT * FROM EXEC Sproc。只需执行 EXEC SPROC。 不,此查询会根据使用情况进一步细化(结果分页等)

以上是关于SQLServer:为啥要避免使用表值用户定义函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在此 SQL Server 内联表值函数上收到 11555 错误?

SQL之用户自定义函数

SQL之用户自定义函数

sqlserver中的表值函数和标量值函数

EF:从 C# 将表值参数传递给用户定义的函数

为啥 SQL Server 表值函数插入需要很长时间?