【发布时间】:2026-01-07 09:10:01
【问题描述】:
场景
快速背景知识:我正在尝试优化内联表值函数
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 秒内返回约 700k 行。当服务器上没有其他用户活动时,后者(第二个 SQL 块)在大约十分钟内返回相同数量的行。也许值得注意的是有一个 WHERE 子句,但是为了简单起见,我在这里省略了它;两个查询都是一样的。
执行计划
下面是第一个的执行计划。如前所述,它享有自动并行性,而后一个查询不值得在此处显示,因为它非常庞大(扩展了整个 iTVF 和底层视图、子查询)。无论如何,后者在任何程度上也不会并行执行(AFAIK)。
我的问题
- 是否可以在没有临时表的情况下实现与第一个块相当的性能?
- 也就是说,具有较慢 SQL 的相对简单性和人类可读性。
- 为什么加入临时表比加入 iTVF 更快?
- 为什么使用临时表比使用相同方式填充的内存表更快?
除了这些明确的问题之外,如果有人能指出我正确的方向以更好地理解这一点,那么我将非常感激。
【问题讨论】:
-
在某种程度上取决于您的 sql server 版本,但临时表的平均统计数据比 TVF 单行的估计值要好...
-
我认为问题不在于“内存表”——它的执行速度与临时表一样快。问题在于用户定义的函数——它们是逐行执行的,这比标准的 SQL 集合操作要慢得多。仅当该任务没有其他选项时,我才会使用 iTVF。您的 SQL 代码的可读性是一个主观因素 - 您可以通过使用更有意义/直观的名称(即“#tC”? - 我敢打赌这个表有一个更好的名称 :)
-
贴出你的函数代码可能会有所帮助...
标签: sql-server tsql