SQL Server 表值函数和异常组合性能

Posted

技术标签:

【中文标题】SQL Server 表值函数和异常组合性能【英文标题】:SQL Server Table-Value Function and Except Combination performance 【发布时间】:2013-02-20 08:15:41 【问题描述】:

我有一个表(包含大约 18000 条记录的资源)和一个带有此主体的表值函数:

ALTER FUNCTION [dbo].[tfn_GetPackageResources]
(   
    @packageId int=null,
    @resourceTypeId int=null,
    @resourceCategoryId int=null,
    @resourceGroupId int=null,
    @resourceSubGroupId int=null
)
RETURNS TABLE 
AS
RETURN 
(

    SELECT Resources.*
    FROM    Resources 
            INNER JOIN ResourceSubGroups ON Resources.ResourceSubGroupId=ResourceSubGroups.Id
            INNER JOIN ResourceGroups ON ResourceSubGroups.ResourceGroupId=ResourceGroups.Id
            INNER JOIN ResourceCategories ON ResourceGroups.ResourceCategoryId=ResourceCategories.Id
            INNER JOIN ResourceTypes ON ResourceCategories.ResourceTypeId=ResourceTypes.Id
    WHERE
        (@resourceSubGroupId IS NULL OR ResourceSubGroupId=@resourceSubGroupId) AND
        (@resourceGroupId IS NULL OR ResourceGroupId=@resourceGroupId) AND
        (@resourceCategoryId IS NULL OR ResourceCategoryId=@resourceCategoryId) AND
        (@resourceTypeId IS NULL OR ResourceTypeId=@resourceTypeId) AND
        (@packageId IS NULL OR PackageId=@packageId) 



)

现在我做一个这样的查询:

SELECT  id
FROM    dbo.tfn_GetPackageResources(@sourcePackageId,null,null,null,null)
WHERE   id not in(  
    SELECT  a.Id 
    FROM    dbo.tfn_GetPackageResources(@sourcePackageId,null,null,null,null) a INNER JOIN
            dbo.tfn_GetPackageResources(@comparePackageId,null,null,null,null) b
            ON  a.No = b.No AND 
                a.UnitCode=b.UnitCode AND 
                a.IsCompound=b.IsCompound AND 
                a.Title=b.Title
    )

这个查询大约需要 10 秒!(虽然每个部分查询运行得非常快,但整个查询都需要时间)我用 LEFT JOINNOT EXISTS 检查它,但结果是一样的。 但如果我直接在 Resources 表上运行查询,它只需要一秒钟或更短的时间!快速查询是:

select * from resources where id not in (select id from resources)

我该如何解决?

【问题讨论】:

只做SELECT * FROM dbo.tfn_GetPackageResources(@sourcePackageId,null,null,null,null)有多快? 不看执行计划很难找出原因。但很可能是因为优化器认为表值函数只返回一行并选择了一个效率低下的计划。 【参考方案1】:

您的 UDF 像宏一样展开。

所以你的完整查询有

IN 子句中的 9 个 INNER JOIN 主 SELECT 中有 4 个 INNER JOIN。 您对每个 WHERE 子句总共应用 (... IS NULL OR ...) 15 次。

由于这种扩展,您对巧妙代码重用的想法失败了SQL 通常不适合这种重用。

保持简单:

SELECT
    R.id
FROM
    Resources R
WHERE
    R.PackageId = @sourcePackageId
    AND 
    R.id not in (  
        SELECT  a.Id 
       FROM    Resources  a
               INNER JOIN
               Resources  b
                   ON  a.No = b.No AND 
                        a.UnitCode=b.UnitCode AND 
                        a.IsCompound=b.IsCompound AND 
                        a.Title=b.Title
        WHERE
             a.PackageId = @sourcePackageId
             AND
             b.PackageId = @comparePackageId
    )

有关更多信息,请在此处查看我的其他答案:

Why is a UDF so much slower than a subquery? Profiling statements inside a User-Defined Function Does query plan optimizer works well with joined/filtered table-valued functions? Table Valued Function where did my query plan go?

【讨论】:

【参考方案2】:

在您的函数中,声明它返回的表的类型,并包含一个主键。这样,ID 过滤器将能够更有效地查找 ID。

语法见http://msdn.microsoft.com/en-us/library/ms191165(v=sql.105).aspx。

【讨论】:

这使得每个 UDF 运行完成,没有扩展。大多数情况下。它很可能会运行得更糟。请参阅我的答案以获取解释原因的链接【参考方案3】:

你应该尝试将一个复杂的查询分解成多个简单的查询,将它们的结果存储在临时表中,这样一个复杂的执行计划将被几个简单的计划取代,这些计划的总执行时间可能比一个复杂的执行计划:

SELECT  *
INTO    #temp1
FROM    dbo.tfn_GetPackageResources(@sourcePackageId,null,null,null,null)

SELECT  *
INTO    #temp2
FROM    dbo.tfn_GetPackageResources(@comparePackageId,null,null,null,null)

SELECT  a.Id 
INTO    #ids
FROM    #temp1 a 
INNER JOIN
        #temp2 b ON  
        a.No = b.No 
AND     a.UnitCode=b.UnitCode 
AND     a.IsCompound=b.IsCompound 
AND     a.Title=b.Title

SELECT  id
FROM    #temp1
WHERE   id not in(  
    SELECT  Id 
    FROM    #ids
)

-- you can also try replacing the above query with this one if it performs faster
SELECT  id
FROM    #temp1 t
WHERE   NOT EXISTS 
(
    SELECT Id FROM #ids i WHERE i.Id = t.id
)

【讨论】:

以上是关于SQL Server 表值函数和异常组合性能的主要内容,如果未能解决你的问题,请参考以下文章

sql server表值函数和视图如何一起使用

SQL Server:表值函数与存储过程

使用 Inline 表值函数封装 SQL 代码的性能

SQL Server 中标量、表值和聚合函数之间的区别?

在 SQL Server 的视图中使用表值函数

SQL SERVER函数之深入表值函数的处理分析