调用 T-SQL 表值函数比调用一次快多次?

Posted

技术标签:

【中文标题】调用 T-SQL 表值函数比调用一次快多次?【英文标题】:Calling T-SQL table-valued function multiple times faster than calling once? 【发布时间】:2016-06-02 22:57:44 【问题描述】:

我对某事感到困惑。我正在使用 T-SQL 并尝试优化如下所示的代码:

SELECT * FROM MyTable mt
LEFT JOIN
  (SELECT * FROM Table1 t1
   LEFT JOIN api.tableValuedFunc(@someArgs) tvf
   on tvf.Key = t1.Key) firstJoin
on mt.Key = firstJoin.key
LEFT JOIN
  (SELECT * FROM Table2 t2
   LEFT JOIN api.tableValuedFunc(@someArgs) tvf
   on tvf.Key = t2.Key) secondJoin
on mt.Key = secondJoin.key

通过这样做:

declare @tvfResult TABLE ( ... some columns ... )
insert into @tvfResults SELECT * FROM api.tableValuedFunc(@someArgs);

SELECT * FROM MyTable mt
LEFT JOIN
  (SELECT * FROM Table1 t1
   LEFT JOIN @tvfResults tvf
   on tvf.Key = t1.Key) firstJoin
on mt.Key = firstJoin.key
LEFT JOIN
  (SELECT * FROM Table2 t2
   LEFT JOIN @tvfResults tvf
   on tvf.Key = t2.Key) secondJoin
on mt.Key = secondJoin.key

这使执行时间加倍!为什么是这样?在第二个例子中,我(天真地)假设我执行函数的频率是原来的一半。我正在破坏的幕后是否发生了某种 SQL 魔法?

【问题讨论】:

环顾四周,这似乎与***.com/questions/4109152/… 有关,但这种情况下的处方是使用临时表,这就是我正在做的。 好的,显然这不是一个临时表,而是一个表变量,它慢了很多。不过,这一切都需要存在于一个函数中,所以我不能使用临时表。选项 #1 是否与我得到的一样快? 请确认您是否在测量的查询中实际使用“SELECT *”,或者只是在问题中使用它作为简写。请注意,由于任何覆盖索引不可用,仅使用“SELECT *”可能会严重影响性能。接下来,请附上来自 SSMS 的 ACTUAL QUERY PLAN 以供分析 - 如果没有查询计划或涉及的所有表/视图/等的完整 DDL,我们如何才能确定正在发生的事情? 【参考方案1】:

如果要输出大量行,创建本地临时表可能会获得更好的性能,而不是创建表变量。

create table #tvfResult  ( ... some columns ... )

insert into #tvfResults SELECT * FROM api.tableValuedFunc(@someArgs);

SELECT * FROM MyTable mt
LEFT JOIN
  (SELECT * FROM Table1 t1
   LEFT JOIN #tvfResults tvf
   on tvf.Key = t1.Key) firstJoin
on mt.Key = firstJoin.key
LEFT JOIN
  (SELECT * FROM Table2 t2
   LEFT JOIN #tvfResults tvf
   on tvf.Key = t2.Key) secondJoin
on mt.Key = secondJoin.key

本地临时表仅对它们的创建者可见,在与 SQL Server 实例的同一连接期间,与首次创建或引用表时一样。用户断开与 SQL Server 实例的连接后,本地临时表将被删除。

如果您使用同一会话重新运行查询,请显式删除临时表。

drop table #tvfResult

【讨论】:

我想这样做,但所有这些都需要存在于函数本身中,因此很遗憾,临时表是禁止使用的。 你可以在函数内部创建一个本地临时表,不是吗? 我是这么想的,但是 SQL server 抛出一个错误:Cannot access temporary tables from within a function. tvf inside tvf - 开始气味不好。尝试重新考虑您尝试实施解决方案的方法和主要思想。 我继承了这段代码,目前正在评估重构。【参考方案2】:

请注意,除非您从问题中省略了几个关键因素,否则您的第一个查询将简化为:

SELECT * FROM MyTable mt
LEFT JOIN Table1 t1 on mt.Key = t1.key
LEFT JOIN Table2 t2 on mt.Key = t2.key
LEFT JOIN api.tableValuedFunc(@someArgs) tvf1 on tvf1.Key = t1.key
LEFT JOIN api.tableValuedFunc(@someArgs) tvf2 on tvf2.Key = t2.key

现在,分析减少到您在 SELECT 子句中的实际列选择 - 请提供这些详细信息,因为如果不知道查询是什么就不可能分析您的查询 - 非常详细 - 因为性能问题总是在详情。

【讨论】:

由于所讨论的函数超过 600 行,我省略了相当多的内容。这是它的执行计划:pastebin.com/7Y4axtKB 我的优化错误:pastebin.com/NZuZHkY1

以上是关于调用 T-SQL 表值函数比调用一次快多次?的主要内容,如果未能解决你的问题,请参考以下文章

用T-SQL语句自定义一表值函数

为啥比较 char 变量两次比比较短变量一次快

T-SQL编程 —— 用户自定义函数(内嵌表值函数)

等效于 Amazon Redshift 中的 T-SQL 表值构造函数?

将 T-SQL 表值函数字符串拆分为 C#

T-SQL 多语句表值函数