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 JOIN
和 NOT 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 表值函数和异常组合性能的主要内容,如果未能解决你的问题,请参考以下文章