调用 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 表值函数比调用一次快多次?的主要内容,如果未能解决你的问题,请参考以下文章