内联表值函数中SELECT *的性能

时间:2019-03-01 06:46:15

标签: sql-server tsql stored-functions set-returning-functions

今天,在工作的首席DBA表示我不应该使用ITVF完全包装视图,但是从我的基本基准来看,我持怀疑态度。看起来SQL Server只是在查询时整理出它实际需要的那些列(基于该函数的请求)。我之所以这样说,是因为我看到下面两个示例之间的执行时间非常相似。


uf_GetCustomersByCity_A

在此示例中,我创建了一个执行SELECT *并返回过滤后的CustomerView的ITVF。

CREATE FUNCTION [dbo].[uf_GetCustomersByCity_A] (@idCity INT)
RETURNS TABLE
AS RETURN

    SELECT CustView.*
      FROM [dbo].[CustomerView] CustView
     WHERE CustView.idCity = @idCity

GO

uf_GetCustomersByCity_B

CREATE FUNCTION [dbo].[uf_GetCustomersByCity_B] (@idCity INT)
RETURNS TABLE
AS RETURN

    SELECT CustView.idCustomer
         , CustView.cFullName
         , CustView.cCityName
         , CustView.fBalance
      FROM [dbo].[CustomerView] CustView
     WHERE CustView.idCity = @idCity

GO

我的问题是,这是否是一个有效的观察,还是仅仅是许多小时的调试的副作用(假设SQL Server在使用时进行了优化)。在View中提供所需的所有内容,而不是在ITVF中专门指定每一列,具有很大的价值。

业余基准

因此,两者都运行良好,在4-5秒钟内可产生约50万行(注意:有些复杂的子句会延长执行时间,这些示例在这里很难说明目的)。该视图有大约70或80列,其中许多是直接格式化或内联操作的。

-- Around 500k rows in ~3-4 seconds:

SELECT idCustomer, cCityName
FROM [dbo].[uf_GetCustomersByCity_A](93)

-- Around 500k rows, again ~3-4 seconds:

SELECT idCustomer, cCityName
FROM [dbo].[uf_GetCustomersByCity_B](93)

开发箱上的性能相同,但是目前没有人在使用它。假设cFullNamecGivenNamecFamilyName的串联,而cCityName的返回与存储的完全相同。向查询中添加cCityName的影响明显小于cFullName,这使我相信我注意到这不是SSMS的交付时间。

-- Around 500k rows, ~6 seconds:

SELECT idCustomer, cFullName
FROM [dbo].[uf_GetCustomersByCity_A](93)

-- Around 500k rows, ~6 seconds:

SELECT idCustomer, cFullName
FROM [dbo].[uf_GetCustomersByCity_B](93)

我的想法是,如果SELECT *在ITVF中很重要,那么它将花费大量时间来确定不使用的列的值。从我制定的快速基准测试中,我发现通过SELECT *包装整个View而不是一次指定一个列,根本没有太大的区别,本质上是重述了View的结构。我的直觉在这里有效吗?

1 个答案:

答案 0 :(得分:2)

i中的iTVF用于内联-如您所知。这意味着,引擎将尝试找到最佳执行计划,就像直接将语句写入查询

从这个角度来看,无论您是否使用

SELECT * FROM YourView WHERE idCity=@idCity

SELECT * FROM YourITVF(@idCity)

引擎应该足够聪明,只处理所需的列,但是-通常-最好使用列的固定列表。 (请参阅@a_horse_with_no_name注释中的链接。)

提示:使用SELECT * FROM ...包装视图(根据需要)时,请记住,如果更改视图,则必须重新编译该iTVF。

问题可能是引擎难以解析深层嵌套的结构,最终可能找不到最佳计划(甚至可能看不到,最终结果中不需要昂贵的计算列)。 / p>

如果您的视图基于子视图,而这些子视图是基于子子视图,其他iTVF等构建的,则将导致次优计划。

几天前,我不得不调整慢速视图,结果显示为具有9(!)调用级别的视图,涵盖了...中视图的视图以及许多计算列等等。引擎无法再浏览这个丛林了。

简而言之:

  • 尽量不要嵌套太深。
  • iTVF可以使代码更具可读性(重复次数更少,说话的名字
  • iTVF可以带来更好的性能(因为它已预先编译了一组固定的参数,但要注意参数嗅探
  • 我不会仅使用iTVF来传递简单的过滤器变量...