SQL 递归层次结构非常慢
SQL recursive hierarchy very slow
所以我刚接到一项任务来优化 SQL 查询,该查询构建产品、内部产品、制造工厂操作、操作详细信息和项目(制造产品所需)的层次结构树。查询 returns 218k 行,工作正常但需要大约 15-18 秒来构建树。是否可以优化该查询,使其大约需要 2-3 秒?如果我添加 @ProductID 作为此过程的参数并执行
WHERE ProductID = @ProductID
从产品 table 中选择后,它工作得非常快,但是树不完整,因为如果我为其构建树的产品是内部产品,它的父产品不会显示在那个树。
将所有这些 table 加载到 C# 应用程序并在那里构建所有层次结构会更快吗?
我乐于接受所有想法。下面是有问题的查询
ALTER PROCEDURE [dbo].[ProductProduct_selectHierarchy_v2]
WITH EXECUTE AS CALLER
AS
BEGIN
WITH ProductProductHierarchy (
ProductID
,ItemProductID
,LEVEL
,UniqueID
,ParentID
,RowType
,ProductDetailID
,ProductProductDetailBindID
,ManufactoryOperationID
,ItemID
)
AS (
-- Anchor member definition
SELECT dbo.Product.ProductID AS ProductID
,Product.ProductID AS ItemProductID
,0 AS LEVEL
,Cast(cast(Product.ProductID AS NVARCHAR(40)) + '' AS NVARCHAR(50)) AS UniqueID
,cast('' AS NVARCHAR(50)) AS ParentID
,1 AS RowType
,NULL AS ProductDetailID
,NULL AS ProductProductDetailBindID
,NULL AS ManufactoryOperationID
,NULL AS ItemID
FROM dbo.Product
UNION ALL
-- Recursive member definition
SELECT e.ProductID
,e.ItemProductID
,LEVEL + 1
,Cast(Cast(d.UniqueID AS NVARCHAR(40)) + '_' + cast((e.ItemProductID) AS NVARCHAR(10)) AS NVARCHAR(50)) AS UniqueID
,Cast(d.UniqueID AS NVARCHAR(50)) AS ParentID
,1 AS RowType
,NULL AS ProductDetailID
,NULL AS ProductProductDetailBindID
,NULL AS ManufactoryOperationID
,NULL AS ItemID
FROM dbo.ProductProduct AS e
INNER JOIN ProductProductHierarchy AS d ON e.ProductID = d.ItemProductID
UNION ALL
SELECT NULL AS ProductID
,NULL
,LEVEL + 1
,Cast(Cast(d.UniqueID AS NVARCHAR(40)) + '_' + cast((e.ProductDetailID) AS NVARCHAR(10)) AS NVARCHAR(50)) AS UniqueID
,Cast(d.UniqueID AS NVARCHAR(50)) AS ParentID
,2 AS RowType
,e.ProductDetailID AS ProductDetailID
,e.ProductProductDetailBindID AS ProductProductDetailBindID
,NULL AS ManufactoryOperationID
,NULL AS ItemID
FROM dbo.ProductProductDetailBind AS e
INNER JOIN ProductProductHierarchy AS d ON e.ProductID = d.ItemProductID
UNION ALL
SELECT NULL AS ProductID
,NULL
,LEVEL + 1
,Cast(Cast(d.UniqueID AS NVARCHAR(40)) + '_' + cast((e.ManufactoryOperationID) AS NVARCHAR(10)) AS NVARCHAR(50)) AS UniqueID
,Cast(d.UniqueID AS NVARCHAR(50)) AS ParentID
,3 AS RowType
,NULL AS ProductDetailID
,NULL AS ProductProductDetailBindID
,e.ManufactoryOperationID AS ManufactoryOperationID
,NULL AS ItemID
FROM dbo.ProductDetailOperation AS e
INNER JOIN ProductProductHierarchy AS d ON e.ProductProductDetailBindID = d.ProductProductDetailBindID
UNION ALL
SELECT NULL AS ProductID
,NULL
,LEVEL + 1
,Cast(Cast(d.UniqueID AS NVARCHAR(40)) + '_' + cast((e.ItemID) AS NVARCHAR(10)) AS NVARCHAR(50)) AS UniqueID
,Cast(d.UniqueID AS NVARCHAR(50)) AS ParentID
,4 AS RowType
,NULL AS ProductDetailID
,NULL AS ProductProductDetailBindID
,NULL AS ManufactoryOperationID
,e.ItemID AS ItemID
FROM dbo.ProductItem AS e
INNER JOIN ProductProductHierarchy AS d ON e.ProductID = d.ItemProductID
)
-- Statement that executes the CTE
SELECT CASE
WHEN RowType = 1
THEN ProductName + '<' + ProductCode + '>'
WHEN RowType = 2
THEN ProductDetail.ProductDetailName
WHEN RowType = 3
THEN ManufactoryOperation.ManufactoryOperationName
ELSE Item.ItemName
END AS ProductName
,UniqueID
,ProductProductHierarchy.ParentID
,Product.ProductID
,RowType
FROM ProductProductHierarchy
LEFT OUTER JOIN Product ON Product.ProductID = ProductProductHierarchy.ItemProductID
AND RowType = 1
LEFT OUTER JOIN ProductDetail ON ProductDetail.ProductDetailID = ProductProductHierarchy.ProductDetailID
AND RowType = 2
LEFT OUTER JOIN ManufactoryOperation ON ManufactoryOperation.ManufactoryOperationID = ProductProductHierarchy.ManufactoryOperationID
AND RowType = 3
LEFT OUTER JOIN Item ON Item.ItemID = ProductProductHierarchy.ItemID
AND RowType = 4
OPTION (MAXRECURSION 0)
结束
看起来您正在有效地构建一组异构行,我通常会尽量远离这些行。如果需要,使用递归获取产品树,然后将操作、项目等作为单独的结果集引入。像您一样混合行类型很可能会导致令人头疼的问题(不仅仅是性能问题)。
在不了解您的应用程序的全部功能的情况下,我不能说出您应该从数据库中检索这些项目的最佳方式,但我肯定会从这里开始作为一个可能的问题。
所以我刚接到一项任务来优化 SQL 查询,该查询构建产品、内部产品、制造工厂操作、操作详细信息和项目(制造产品所需)的层次结构树。查询 returns 218k 行,工作正常但需要大约 15-18 秒来构建树。是否可以优化该查询,使其大约需要 2-3 秒?如果我添加 @ProductID 作为此过程的参数并执行
WHERE ProductID = @ProductID
从产品 table 中选择后,它工作得非常快,但是树不完整,因为如果我为其构建树的产品是内部产品,它的父产品不会显示在那个树。 将所有这些 table 加载到 C# 应用程序并在那里构建所有层次结构会更快吗?
我乐于接受所有想法。下面是有问题的查询
ALTER PROCEDURE [dbo].[ProductProduct_selectHierarchy_v2]
WITH EXECUTE AS CALLER
AS
BEGIN
WITH ProductProductHierarchy (
ProductID
,ItemProductID
,LEVEL
,UniqueID
,ParentID
,RowType
,ProductDetailID
,ProductProductDetailBindID
,ManufactoryOperationID
,ItemID
)
AS (
-- Anchor member definition
SELECT dbo.Product.ProductID AS ProductID
,Product.ProductID AS ItemProductID
,0 AS LEVEL
,Cast(cast(Product.ProductID AS NVARCHAR(40)) + '' AS NVARCHAR(50)) AS UniqueID
,cast('' AS NVARCHAR(50)) AS ParentID
,1 AS RowType
,NULL AS ProductDetailID
,NULL AS ProductProductDetailBindID
,NULL AS ManufactoryOperationID
,NULL AS ItemID
FROM dbo.Product
UNION ALL
-- Recursive member definition
SELECT e.ProductID
,e.ItemProductID
,LEVEL + 1
,Cast(Cast(d.UniqueID AS NVARCHAR(40)) + '_' + cast((e.ItemProductID) AS NVARCHAR(10)) AS NVARCHAR(50)) AS UniqueID
,Cast(d.UniqueID AS NVARCHAR(50)) AS ParentID
,1 AS RowType
,NULL AS ProductDetailID
,NULL AS ProductProductDetailBindID
,NULL AS ManufactoryOperationID
,NULL AS ItemID
FROM dbo.ProductProduct AS e
INNER JOIN ProductProductHierarchy AS d ON e.ProductID = d.ItemProductID
UNION ALL
SELECT NULL AS ProductID
,NULL
,LEVEL + 1
,Cast(Cast(d.UniqueID AS NVARCHAR(40)) + '_' + cast((e.ProductDetailID) AS NVARCHAR(10)) AS NVARCHAR(50)) AS UniqueID
,Cast(d.UniqueID AS NVARCHAR(50)) AS ParentID
,2 AS RowType
,e.ProductDetailID AS ProductDetailID
,e.ProductProductDetailBindID AS ProductProductDetailBindID
,NULL AS ManufactoryOperationID
,NULL AS ItemID
FROM dbo.ProductProductDetailBind AS e
INNER JOIN ProductProductHierarchy AS d ON e.ProductID = d.ItemProductID
UNION ALL
SELECT NULL AS ProductID
,NULL
,LEVEL + 1
,Cast(Cast(d.UniqueID AS NVARCHAR(40)) + '_' + cast((e.ManufactoryOperationID) AS NVARCHAR(10)) AS NVARCHAR(50)) AS UniqueID
,Cast(d.UniqueID AS NVARCHAR(50)) AS ParentID
,3 AS RowType
,NULL AS ProductDetailID
,NULL AS ProductProductDetailBindID
,e.ManufactoryOperationID AS ManufactoryOperationID
,NULL AS ItemID
FROM dbo.ProductDetailOperation AS e
INNER JOIN ProductProductHierarchy AS d ON e.ProductProductDetailBindID = d.ProductProductDetailBindID
UNION ALL
SELECT NULL AS ProductID
,NULL
,LEVEL + 1
,Cast(Cast(d.UniqueID AS NVARCHAR(40)) + '_' + cast((e.ItemID) AS NVARCHAR(10)) AS NVARCHAR(50)) AS UniqueID
,Cast(d.UniqueID AS NVARCHAR(50)) AS ParentID
,4 AS RowType
,NULL AS ProductDetailID
,NULL AS ProductProductDetailBindID
,NULL AS ManufactoryOperationID
,e.ItemID AS ItemID
FROM dbo.ProductItem AS e
INNER JOIN ProductProductHierarchy AS d ON e.ProductID = d.ItemProductID
)
-- Statement that executes the CTE
SELECT CASE
WHEN RowType = 1
THEN ProductName + '<' + ProductCode + '>'
WHEN RowType = 2
THEN ProductDetail.ProductDetailName
WHEN RowType = 3
THEN ManufactoryOperation.ManufactoryOperationName
ELSE Item.ItemName
END AS ProductName
,UniqueID
,ProductProductHierarchy.ParentID
,Product.ProductID
,RowType
FROM ProductProductHierarchy
LEFT OUTER JOIN Product ON Product.ProductID = ProductProductHierarchy.ItemProductID
AND RowType = 1
LEFT OUTER JOIN ProductDetail ON ProductDetail.ProductDetailID = ProductProductHierarchy.ProductDetailID
AND RowType = 2
LEFT OUTER JOIN ManufactoryOperation ON ManufactoryOperation.ManufactoryOperationID = ProductProductHierarchy.ManufactoryOperationID
AND RowType = 3
LEFT OUTER JOIN Item ON Item.ItemID = ProductProductHierarchy.ItemID
AND RowType = 4
OPTION (MAXRECURSION 0)
结束
看起来您正在有效地构建一组异构行,我通常会尽量远离这些行。如果需要,使用递归获取产品树,然后将操作、项目等作为单独的结果集引入。像您一样混合行类型很可能会导致令人头疼的问题(不仅仅是性能问题)。
在不了解您的应用程序的全部功能的情况下,我不能说出您应该从数据库中检索这些项目的最佳方式,但我肯定会从这里开始作为一个可能的问题。