表值函数导致我的查询性能下降

sca*_*cci 22 sql-server sql-server-2005 sql-server-2008

今天我有一个可怕的时间试图获得一个查询来执行我期望的方式.我不得不对昨天存在于查询中的表值函数稍作修改,并且该更改对查询产生了巨大的性能影响.在评估执行计划并查看统计信息IO和时间后,我发现由于我更改了函数以返回表变量而不仅仅是结果集,因此它正在对正在查询的其中一个表执行完全扫描.

我的问题是为什么让它返回表(TableVariable)而不仅仅是选择/结果集导致计划发生如此大的变化?

难倒....

Ada*_*Dev 57

返回一个表变量将使它成为一个多语句表值函数,并且可能对性能有害,因为它被视为一个表,除了没有可用于SQL Server的统计数据以建立良好的执行计划 - 所以它将估计函数返回非常少量的行.如果它返回的行数较多,那么生成的计划可能远远低于最优.

然而,仅返回SELECT使其成为内联表值函数 - 将其视为视图.在这种情况下,实际的基础表将被带入主查询,并且可以基于适当的统计信息生成更好的执行计划.您会注意到,在这种情况下,执行计划根本不会提及函数,因为它基本上只是将函数合并到主查询中.

CSS SQL Server工程师在MSDN上有很好的参考,包括(引用):

但是如果你使用多语句TVF,它就像另一个表一样对待.由于没有可用的统计信息,SQL Server必须做出一些假设,并且通常会提供较低的估计值.如果您的TVF只返回几行,那就没问题了.但是如果你打算用数千行填充TVF并且如果这个TVF与其他表连接,那么低基数估计可能导致效率低下的计划.

  • @charles Bretana是的,但你在谈论从内联标量UDF到表值UDF.OP已经从内联表值函数变为多语句表值UDF.所以我的答案在这个场景/上下文中是正确的,并且downvote是不合理的 (4认同)
  • 添加了引用CSS文章的引用,它比我更好地解释:)我不得不同意downvote. (3认同)

小智 6

在 SQL Server 2014 上,我们可以通过将表值函数数据插入临时表然后对其进行连接来解决我们的问题。而不是直接与表值函数进行连接。

这将我们的执行时间从 2 分钟缩短到 4 秒。

这是一个适用于我们团队的示例:

--慢速查询(2 分钟):

DECLARE @id INT = 1;

SELECT * 
FROM [data].[someTable] T
INNER JOIN [data].[tableValueFunction](@id) TVF ON TVF.id = T.id;
Run Code Online (Sandbox Code Playgroud)

--快速查询(4秒):

DECLARE @id INT = 1;

SELECT * 
INTO #tableValueFunction
FROM [data].[tableValueFunction](@id) TVF

SELECT * 
FROM [data].[someTable] T
INNER JOIN #tableValueFunction TVF ON TVF.id = T.id;
Run Code Online (Sandbox Code Playgroud)


Cha*_*ana 5

这是因为多语句表值UDF无法与其使用的SQL statememnt内联处理,因此不能成为语句缓存计划的一部分.这意味着它必须与其余的一部分单独编译对于查询生成的最终结果集中的每一行,它一遍又一遍地使用它.

一个内联表值UDF,OTOH,处理与使用它的SQL编译一起,因此成为部分缓存的计划,只得到处理和编译一次,不管你有多少行产生.