如何在此sql查询中获取所有祖先,所选类别和直接子项

时间:2017-07-25 23:44:46

标签: sql-server common-table-expression ancestor

我希望获得所有祖先,选定的类别和直接儿童,并计算每个类别及其所有孩子的所有项目。

这就是我所做的:

DECLARE @CategoryId INT = 8
;WITH Re AS
(
SELECT CategoryId, Name, ParentId, CategoryId RootId, 0 Depth, Sort = CAST(Name AS VARCHAR(MAX))
    FROM Category
WHERE CategoryId = @CategoryId OR ParentId = @CategoryId
UNION ALL
SELECT C.CategoryId, C.Name, C.ParentId, RootId, Depth = Re.Depth + 1, Sort = Re.Sort + CAST(C.Name AS VARCHAR(200))
    FROM Re JOIN Category C ON Re.CategoryId = C.ParentId
)
SELECT Y.CategoryId, Y.Name, Y.ParentId, X.CatCount
    FROM (SELECT RootId, COUNT(I.CategoryId) catCount
            FROM Re LEFT OUTER JOIN Item I ON Re.CategoryId = I.CategoryId
          GROUP BY RootId) X
    JOIN (SELECT Re.CategoryId, Re.ParentId, Name, MAX(Depth) MaxDepth
            FROM Re
          GROUP BY Re.CategoryId, Re.ParentId, Name) Y ON Y.CategoryId = X.RootId
ORDER BY CategoryId

它返回我想要的东西,但是它有一个小问题。例如,当@CategoryId = 8时,此查询应显示:

CategoryId  Name    ParentId    CatCount
    0        A        NULL         16
    1        B         0           10
    7        H         1           4
    8        I         7           2
    13       N         8           1

但它返回:

CategoryId  Name    ParentId    CatCount
    8        I         7           2
    13       N         8           1

选择子类别时,不会显示祖先。

  1. 哪里有问题?
  2. 如何优化此查询?
  3. 我会感谢有人帮助我

1 个答案:

答案 0 :(得分:0)

由于RootId的问题,您的查询无法按照您的描述运行(请参阅下面的评论) 您的CTE需要更改为:

DECLARE @CategoryId INT = 8
;WITH Re AS
(
    -- Anchor (this is the starting category)
    SELECT CategoryId, Name, ParentId, CategoryId AS RootId,
        0 AS Depth, Sort = CAST(Name AS VARCHAR(MAX)), CONVERT( VARCHAR( 20 ), 'Root' ) AS Relation
    FROM Category
    WHERE CategoryId = @CategoryId
    UNION ALL
    -- Find children
    SELECT C.CategoryId, C.Name, C.ParentId, C.CategoryId AS RootId, -- Fixed RootId problem
        Depth = Re.Depth + 1, Sort = Re.Sort + CAST(C.Name AS VARCHAR(200)), CONVERT( VARCHAR( 20 ), 'Child' ) AS Relation
    FROM Re
        INNER JOIN Category C ON Re.CategoryId = C.ParentId
    WHERE Re.Relation IN( 'Root', 'Child' )
        -- I do not think this is necessary as we have established the anchor, see above
        AND C.ParentId = @CategoryId
    UNION ALL
    -- Find Ancestors (Parents)
    SELECT C.CategoryId, C.Name, C.ParentId, C.CategoryId AS RootId, -- Fixed RootId problem
        Depth = Re.Depth - 1, Sort = Re.Sort + CAST(C.Name AS VARCHAR(200)), CONVERT( VARCHAR( 20 ), 'Parent' ) AS Relation
    FROM Re
        INNER JOIN Category C ON C.CategoryId = Re.ParentId
    WHERE Re.Relation IN( 'Root', 'Parent' )
)
SELECT * FROM Re

我使用了以下数据:

CREATE TABLE Category( CategoryId INT, Name VARCHAR( 10 ), ParentID INT )
SELECT * FROM Category
INSERT INTO Category
SELECT 0, 'A', NULL UNION ALL
SELECT 1 , 'B', 0 UNION ALL
SELECT 7 , 'H' , 1 UNION ALL
SELECT 8 , 'I' , 7 UNION ALL
SELECT 13 , 'N' , 8
-- DROP TABLE Category