场景
关于此内容的快速背景:我正在尝试优化内联表值函数
uf_GetVisibleCustomers(@cUserId)
的使用。 iTVF会包装视图CustomerView
,并过滤掉包含所提供的请求用户不允许查看的客户数据的所有行。这样,如果将来某些用户类型的选择标准发生变化,我们就不必在整个SQL代码库中执行一百次(夸张)新条件。性能不是很好,所以我想在鼓励使用iTVF之前先解决此问题。只是在这里更改了数据库对象名称,以便于演示(希望如此)。
在尝试优化我们的iTVF uf_GetVisibleCustomers
时,我注意到以下SQL…
CREATE TABLE #tC ( idCustomer INT )
INSERT #tC
SELECT idCustomer
FROM [dbo].[uf_GetVisibleCustomers]('requester')
SELECT T.fAmount
FROM [Transactions] T
JOIN #tC C ON C.idCustomer = T.idCustomer
…比这里的原始SQL快几个数量级(IMO更易读,可能会使用)……
SELECT T.fAmount
FROM [Transactions] T
JOIN [dbo].[uf_GetVisibleCustomers]('requester') C ON C.idCustomer = T.idCustomer
我不明白为什么会这样。在相当适度的开发服务器上,前者(SQL的顶部块)在17秒内返回了约70万行。当服务器上没有其他用户活动时,后者(SQL的第二个块)将在大约十分钟内返回相同数量的行。也许值得一提的是,这里有一个WHERE子句,但是为了简单起见,我在这里省略了它。这两个查询都相同。
下面是第一个执行计划。如前所述,它享有自动并行性,但后一个查询不值得在这里显示,因为它规模庞大(扩展了整个iTVF和基础视图,子查询)。无论如何,后者也不会在任何程度上并行执行(AFAIK)。
除了那些明确的问题之外,如果有人能指出正确的方向来更好地理解这一点,那么我将不胜感激。
答案 0 :(得分:1)
在没有看到内联函数的DDL的情况下-很难说出问题所在。查看两个查询的实际执行计划也将有所帮助(也许您可以尝试:https://www.brentozar.com/pastetheplan/)。也就是说,我可以提供一些思考的机会。
如上所述,iTVF访问基础表,视图和关联的索引。如果您的统计信息不是最新的,那么您可能会制定一个错误的计划,而临时表不会发生这种情况。也要注意,填充该临时表需要多长时间?
要查看的另一件事(同样,这也是DDL有用的原因)是:Transactions.idCustomer和#TC.idCustomer的数据类型是否相同?我在您发布的计划中看到一个哈希匹配,这对于两个ID之间的联接来说似乎是不好的(嵌套循环或合并联接会更好)。这可能会降低两个查询的速度,但似乎会对利用iTVF的查询产生更大的影响。
再次根据我的经验推测这^^^。可以尝试以下几项快速的操作(不是烫发修复,而是用于故障排除): 1.检查使用iTVF时是否重新编译查询可以加快处理速度(这可能是统计数据不佳或缓存和重复使用执行计划不佳的迹象) 2.尝试对iTVF查询强制执行并行计划。您可以通过使用Adam Machanic的make_parallel()在查询的末尾添加OPTION(QUERYTRACEON 8649)来实现。