为什么 table 值函数的性能优于 select 直接语句?

Why is the performance of table value function better than select direct statement?

我用AdventureWorks2012做了个测试。 我的问题是:为什么 SELECT 语句直接性能低于 table 值函数。我只把 SELECT statemnt 放入 table 价值函数和完全相反的表现。

CREATE FUNCTION [dbo].[atest1]
(
    @iBusinessEntityID  INT
)
RETURNS @t TABLE
(
    [BusinessEntityID]  INT
  , [NationalIDNumber]  NVARCHAR(15)
  , [JobTitle]          NVARCHAR(50)
)
AS
    BEGIN
        INSERT INTO @t
               SELECT 
                   [e].[BusinessEntityID]
                 , [e].[NationalIDNumber]
                 , [e].[JobTitle]
               FROM [HumanResources].[Employee] [e]
               INNER JOIN [Person].[Person] [p]
                    ON [p].[BusinessEntityID] = [e].[BusinessEntityID]
               WHERE [e].[BusinessEntityID] = @iBusinessEntityID;
        RETURN;
    END;

--TEST PERFORMANCE
SELECT 
    *
FROM [dbo].[atest1](5);
GO
SELECT 
    [e].[BusinessEntityID]
  , [e].[NationalIDNumber]
  , [e].[JobTitle]
FROM [HumanResources].[Employee] [e]
INNER JOIN [Person].[Person] [p]
     ON [p].[BusinessEntityID] = [e].[BusinessEntityID]
WHERE [e].[BusinessEntityID] = 5;

通常函数的行为比直接查询更差,但在这种情况下,对于预定义为函数的某些东西,系统可能会存储更好的计划。在这种情况下,系统正在执行 Table 扫描有时在小 tables 上进行的功能,AdventureWorks DB 就是这种情况,它可能比按索引搜索更好。

此外,在您的示例中,只有一次函数调用。 当您在查询中重复调用标量函数(提供的示例是针对 table 函数)时,函数性能下降的原因尤其明显。

这里的问题是 SSMS 中的估计计划经常显示 错误的百分比 ,在 UDF 的情况下它几乎总是错误的。

cost percentage 是该操作与其他操作相比的估计成本,但在 UDF 的情况下 SSMS 不检查 UDF 的内部结构。

我在我的服务器上创建了您的 UDF 并向其中添加文本 GUID,因此我可以轻松地 return 此 UDF 的计划:

CREATE FUNCTION [dbo].[atest1] (@iBusinessEntityID int)
RETURNS @t TABLE(BusinessEntityID int,NationalIDNumber nvarchar(15),JobTitle nvarchar(50)) AS
BEGIN
INSERT INTO @t /*3C6A985B-748B-44D4-9F76-1A0866342728*/ -- HERE IS MY GUID
SELECT e.BusinessEntityID, e.NationalIDNumber, e.JobTitle
FROM HumanResources.Employee e INNER JOIN Person.Person p ON p.BusinessEntityID = e.BusinessEntityID
WHERE e.BusinessEntityID = @iBusinessEntityID
RETURN
END

现在我执行这个函数并以这种方式检索它的计划:

select p.query_plan
from sys.dm_exec_cached_plans cp
     cross apply sys.dm_exec_sql_text(cp.plan_handle) t
     cross apply sys.dm_exec_query_plan(cp.plan_handle) p
where cp.objtype = 'Proc'
      and t.text like '%3C6A985B-748B-44D4-9F76-1A0866342728%'

我检查了这个计划,它与您 "direct statement" 的计划完全一样。它的 SELECT 部分是相同的,但是 table 变量中还有 INSERT 和主计划中的 scan。所以你可以清楚地看到你的UDF的成本不能再低了,它等于"direct statement"成本加上INSERT成本加上table变量scan成本

在这种情况下,table 很小并且只有一个 UDF 调用,因此您无法注意到执行时间的差异,但是如果您创建一个循环来执行您的 "direct statement" 更多次并多次调用 UDF,您可能会看到执行时间差异,并且 "direct statement" 会更快。但是 SSMS 无论如何都会坚持 UDF 的低成本。