加入iTVF可以和加入临时表一样快吗?

时间:2019-02-20 03:04:55

标签: sql-server tsql

  

场景

     

关于此内容的快速背景:我正在尝试优化内联表值函数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)。

Screenshot of live execution plan in SSMS

我的问题

  1. 是否可以实现与没有临时表的第一个块相当的性能?
    • 也就是说,较慢的SQL具有相对的简单性和可读性。
  2. 为什么连接到临时表比连接到iTVF快?
  3. 为什么使用临时表比以相同方式填充的内存表更快?

除了那些明确的问题之外,如果有人能指出正确的方向来更好地理解这一点,那么我将不胜感激。

1 个答案:

答案 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)来实现。