和表值函数连接引发的性能问题分析

2020-07-10 08:05:11易采站长站整理

    让我们来看一个简单的例子。下面代码示例是一个简单的和表值函数做Join的例子:
    首先我们创建表值函数,分别为内联表值函数方式和表值函数方式,如代码清单3所示。


--创建表值行数
CREATE FUNCTION tvf_multi_Test ( )
RETURNS @SaleDetail TABLE ( ProductId INT )
AS
BEGIN
INSERT INTO @SaleDetail
SELECT ProductID
FROM Sales.SalesOrderHeader soh
INNER JOIN Sales.SalesOrderDetail sod ON soh.SalesOrderID = sod.SalesOrderID
RETURN
END
--创建内联表值函数
CREATE FUNCTION tvf_inline_Test ( )
RETURNS TABLE
AS
RETURN
SELECT ProductID
FROM Sales.SalesOrderHeader soh
INNER JOIN Sales.SalesOrderDetail sod ON soh.SalesOrderID = sod.SalesOrderID

代码清单3.创建两种不同的函数

现在,我们使用相同的查询,对这两个表值函数进行Join,代码如代码清单4所示。


--表值函数做Join
SELECT c.personid ,
Prod.Name ,
COUNT(*) 'numer of unit'
FROM Person.BusinessEntityContact c
INNER JOIN dbo.tvf_multi_Test() tst ON c.personid = tst.ProductId
INNER JOIN Production.Product prod ON tst.ProductId = prod.ProductID
GROUP BY c.personid ,
Prod.Name

--内联表值函数做Join
SELECT c.personid ,
Prod.Name ,
COUNT(*) 'numer of unit'
FROM Person.BusinessEntityContact c
INNER JOIN dbo.tvf_inline_Test() tst ON c.personid = tst.ProductId
INNER JOIN Production.Product prod ON tst.ProductId = prod.ProductID
GROUP BY c.personid ,
Prod.Name

代码清单4.表值函数和内联表值函数做Join

执行的成本如图1所示。

图1.两种方式的成本

从IO来看,很明显是选择了次优的执行计划,BusinessEntityContact选择了121317次查找,而不是一次扫描。而内联表函数能够正确知道扫描一次的成本远低于一次查找。

那问题的根源是内联表值函数,对于SQL Server来说,和视图是一样的,这意味着内联表值函数可以参与到逻辑执行计划的代数运算(或者是代数树优化)中,这意味着内敛表可以进一步拆分(如图1所示,第二个内联表的查询,执行计划具体知道内敛表中是SalesOrderHeader表和SalesOrderDetail表,由于查询只选择了一列,所以执行计划优化直到可以无需扫描SalesOrderHeader表),对于内联表值函数来说,执行计划可以完整知道所涉及的表上的索引以及相关统计信息等元数据。
另一方面,表值函数,如图1的第一部分所示,表值函数对整个执行计划来说是一个黑箱子,既不知道统计信息,也没有索引。执行计划中不知道表值函数所涉及的表(图1中为#AE4E5168这个临时表,而不是具体的表明),因此对整个执行计划来说该结果集SQL Server会假设返回的结果非常小,当表值函数返回的结果较多时(如本例所示),则会产生比较差的执行计划。

相关文章 大家在看