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

问题描述 投票:0回答:1

今天,工作中的主要DBA表示我不应该使用ITVF来完全包装视图,但是从我的基本基准测试来看,我对此持怀疑态度。看起来SQL Server只是在查询时对它实际需要的列(基于函数请求的内容)进行排序。我这样说是因为我看到下面两个例子之间的执行时间非常相似。


uf_GetCustomersByCity_A

在这个例子中,我创建了一个ITVF,它执行SELECT *,返回一个过滤的CustomerView

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使用优化)。在视图中提供所需的所有内容有很多价值,而不是在ITVF中专门指定每一列。

业余基准

所以两者都工作得很好,在4-5秒内产生~500k行(注意:有很多复杂的条款可以延长执行时间,这些例子很难说明这里的目的)。 View有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的结构。我的预感在这里有效吗?

sql-server tsql stored-functions set-returning-functions
1个回答
2
投票

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

从这个角度来看,无论你使用是否应该没有区别

SELECT * FROM YourView WHERE idCity=@idCity

要么

SELECT * FROM YourITVF(@idCity)

引擎应该足够聪明,只能处理所需的列,但是 - 通常情况下 - 最好使用列的修复列表。 (参见@ a_horse_with_no_name评论中的链接。)

提示:当您使用SELECT * FROM ...包装视图(如您所愿)时,您应该记住,如果您改变视图,则必须重新编译此iTVF。

问题可能是,引擎在解决深层嵌套结构方面存在问题,最终可能找不到最佳计划(甚至可能看不到,最终结果中不需要昂贵的计算列)。

如果您的视图是基于子视图构建的,并且这些子视图是从子视图,其他iTVF等构建的,那么这将导致次优计划。

几天前,我不得不调整一个慢视图,结果显示为具有9(!)调用级别的视图,覆盖视图中视图中的视图...以及大量计算列等等。引擎再也无法透过这个丛林了。

简而言之:

  • 尽量不要深深地筑巢。
  • iTVF可以带来更易读的代码(减少重复次数,说出姓名)
  • iTVF可以带来更好的性能(因为它是使用一组修复参数预编译的,但要注意参数嗅探)
  • 我不会仅使用iTVF来传递一个简单的过滤变量......
© www.soinside.com 2019 - 2024. All rights reserved.