【发布时间】:2017-04-17 15:27:53
【问题描述】:
对不起,如果这是一个有点长的问题,但没有简单的方法来表达它。
我有以下问题
SELECT
S.*
FROM
Stock S
LEFT JOIN
Stock_Category SC ON SC.StockId = S.Id
WHERE
S.Published = 1
AND (@CategoryId IS NULL OR
(SELECT COUNT(*)
FROM GetParentCategoriesByCategoryId(SC.CategoryId)
WHERE Id = @CategoryId) > 0)
在GetParentCategoriesByCategoryId() 内部,我有以下公用表表达式(CTE):
DECLARE @TableOutput TABLE(Id UNIQUEIDENTIFIER,
PosDissectionId INT,
PosFamilyClassId INT,
ParentId UNIQUEIDENTIFIER,
Code NVARCHAR(25),
[Name] NVARCHAR(100),
Description NVARCHAR(1000),
AzureId UNIQUEIDENTIFIER,
Extension NVARCHAR(10),
Visible BIT,
OrderIndex INT,
StockCount INT,
Depth INT)
BEGIN
DECLARE @TotalVisible INT,
@TotalRows INT
;WITH CategoryStructure (Id, ParentId, ParentName, Name, Depth, Visible)
As
(
SELECT
C.Id,
C.ParentId,
CAST('' AS NVARCHAR(500)) AS ParentName,
C.Name,
0 AS Depth,
C.Visible
FROM
Category C
WHERE
Id = @LocalCategoryId
UNION ALL
SELECT
ParentCategory.Id,
ParentCategory.ParentId,
CategoryStructure.Name AS ParentName,
ParentCategory.Name,
CategoryStructure.Depth + 1,
ParentCategory.Visible
FROM
Category ParentCategory
INNER JOIN
CategoryStructure ON ParentCategory.Id = CategoryStructure.ParentId
)
INSERT INTO @TableOutput
SELECT
C.*,
SC.StockCount,
CS.Depth
FROM
CategoryStructure CS
INNER JOIN
Category C ON C.Id = CS.Id
LEFT JOIN
(SELECT CategoryId, COUNT(*) AS StockCount
FROM Stock_Category SC
INNER JOIN Stock S ON S.Id = SC.StockId
WHERE S.Published = 1 AND
((S.WidthMM IS NOT NULL AND
S.HeightMM IS NOT NULL AND
S.DepthMM IS NOT NULL AND
S.WeightG IS NOT NULL)) AND
CategoryId IN(SELECT CategoryId FROM CategoryStructure)
GROUP BY CategoryId
) SC ON SC.CategoryId = CS.Id
WHERE (@IncludeSelf = 1 OR CS.Id != @CategoryId)
SELECT
@TotalVisible = SUM(CONVERT(INT, Visible)),
@TotalRows = COUNT(*)
FROM @TableOutput
IF @TotalVisible <> @TotalRows
DELETE FROM @TableOutput
RETURN
END
我的查询执行计划如下所示。
不幸的是,我对 2000 行的查询时间超过了 7 秒。我相信我已经添加了正确的索引(它似乎表明查询正在使用它们)。
我已经能够将问题缩小到 CTE 中的 LEFT JOIN
SELECT CategoryId, COUNT(*) AS StockCount
FROM Stock_Category SC
INNER JOIN Stock S ON S.Id = SC.StockId
WHERE S.Published = 1 AND blah blah blah....
因为当我移除它时,性能会大幅提升,但到目前为止我只能推断出这些。
我并不期待一个解决方案,因为我知道它基于许多因素,但我远非 SQL 专家,希望有人可以就我可能需要寻找的内容提供任何指导?
表的架构可以在这里找到:https://www.dropbox.com/s/tpetq6fky58fhti/schemas.sql?dl=0
【问题讨论】:
-
你能提供样本数据,涉及的表架构
-
使用Paste The Plan @ brentozar.com 分享您的执行计划,以下是说明:How to Use Paste the Plan。
-
你可能不想
(SELECT COUNT(*) FROM GetParentCategoriesByCategoryId(SC.CategoryId) WHERE Id = @CategoryId) > 0),而是EXISTS (SELECT 1 from GetParentCategoriesByCategoryId(SC.CategoryId) WHERE Id = @CategoryId。此外,如果可以的话,您可能希望摆脱该函数调用。递归 CTE 并不快。 -
您的多语句表值函数在这里伤害了您,我会将其重写为内联表值函数。 When is a SQL function not a function? "If it’s not inline, it’s rubbish." - Rob Farley
-
不是为每个单独的类别 id 调用多语句 TVF,您是否可以预先实现整个层次结构以获得所有类别 id 符合您标准的结果?然后使用类似
SELECT S.* FROM Stock S LEFT JOIN Stock_Category SC ON SC.StockId = S.Id WHERE S.Published = 1 AND (@CategoryId IS NULL OR SC.CategoryId IN (SELECT CategoryId FROM #Cats)) OPTION (RECOMPILE)
标签: sql sql-server performance tsql common-table-expression