SQL Server CTE 递归 - 将树追溯到每个节点的特定树级别

发布于 2024-12-08 17:50:04 字数 1311 浏览 0 评论 0原文

我有两个表,Item(ItemID、CatID、Name)和Category(CatID、ParentID、Name、CatLevel)。 类别是项目所属类别的树形层次结构。该表还跟踪每个节点所在的类别级别。下面是一个示例:

1. Animal (CatLevel = 1)
    2. Dog (CatLevel = 2)
        3. Beagle (CatLevel = 3)
        4. Labrador (CatLevel = 3)
            5. Yellow (CatLevel = 4)
            6. Chocolate (CatLevel = 4)
            7. Black (CatLevel = 4)
    8. Cat (CatLevel = 2)
        9. Tabby (CatLevel = 3)
    10. Horse (CatLevel = 2)
        11. Stallion (CatLevel = 3)

我正在尝试提出一个查询,该查询为我提供了具有常规 CatID 和我指定级别(例如级别 2)的 CatID 的项目列表。示例输出:

Item      CatID    LevelTwoCatID
______    _____    _____________
Dog       2        2
Beagle    3        2
Labrador  4        2
Yellow    5        2
Chocolate 6        2
Black     7        2
Cat       8        8
Tabby     9        8
Horse     10       10
Stallion  11       10

我知道如何追溯到单个节点的类别,但我不知道如何进行更通用的查询来为每个节点提供“LevelTwoCatID”。以下是我的做法:

WITH Tree AS
(
    SELECT * FROM Category WHERE CatID = 6 -- Chocolate lab, change for some other

    UNION ALL

    SELECT * FROM Category AS a
    INNER JOIN Tree t ON a.CatID = t.ParentID
)
SELECT * FROM Tree WHERE CatLevel = 2

如何获取所有节点/叶子的 CatLevel = 2 节点 ID?

谢谢!

I have two tables, Item (ItemID, CatID, Name) and Category (CatID, ParentID, Name, CatLevel). Category is a tree hierarchy of categories that items can be in. The table also tracks what category level each node is in. Here's an example:

1. Animal (CatLevel = 1)
    2. Dog (CatLevel = 2)
        3. Beagle (CatLevel = 3)
        4. Labrador (CatLevel = 3)
            5. Yellow (CatLevel = 4)
            6. Chocolate (CatLevel = 4)
            7. Black (CatLevel = 4)
    8. Cat (CatLevel = 2)
        9. Tabby (CatLevel = 3)
    10. Horse (CatLevel = 2)
        11. Stallion (CatLevel = 3)

I'm trying to come up with a query that gives me a list of items with their regular CatID and a CatID at a level I specify (for example, level 2). Example output:

Item      CatID    LevelTwoCatID
______    _____    _____________
Dog       2        2
Beagle    3        2
Labrador  4        2
Yellow    5        2
Chocolate 6        2
Black     7        2
Cat       8        8
Tabby     9        8
Horse     10       10
Stallion  11       10

I know how to trace back up to the category of a single node, but I don't know how to make a more generic query that will provide the "LevelTwoCatID" for every node. Here's how I'd do it for one:

WITH Tree AS
(
    SELECT * FROM Category WHERE CatID = 6 -- Chocolate lab, change for some other

    UNION ALL

    SELECT * FROM Category AS a
    INNER JOIN Tree t ON a.CatID = t.ParentID
)
SELECT * FROM Tree WHERE CatLevel = 2

How can I get the CatLevel = 2 node ID for all nodes/leaves?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

棒棒糖 2024-12-15 17:50:04

我知道这篇文章已经很老了,但我是这样做的。

在我的表中有几个根类别,因此我使用 @RootId 来指定我要查找哪一个。根类别的 ParentID 设置为 NULL。

如果您的类别表中没有 CatLevel 列 - cte Tree 会为您创建它。如果要经常使用查询,最好将其添加到表中(UPDATE代码在最后注释)。

如果您希望拥有动态列数(不像我的情况那样固定为 5),请为其创建动态 SQL 查询。

DECLARE @RootId INT, @SearchId INT
SET @RootId = 0 
SET @SearchId = 0

--SELECT it.Id, it.ParentId, it.Name, it.RootId FROM ItemTree it 
--WHERE (@RootId = 0 OR it.RootId = @RootId) AND (@SearchId = 0 OR it.Id = @SearchId)

;WITH Tree AS --gets category levels
(
  SELECT 1 as CatLevel, it.Id, it.ParentId, it.Name, it.RootId
  FROM ItemTree it WHERE it.ParentId IS NULL AND (@RootId = 0 OR it.RootId = @RootId) AND (@SearchId = 0 OR it.Id = @SearchId)

  UNION ALL

  SELECT t.CatLevel + 1 as CatLevel, it.Id, it.ParentId, it.Name, it.RootId
  FROM ItemTree it
  INNER JOIN Tree t ON it.ParentId = t.Id
  WHERE (@RootId = 0 OR it.RootId = @RootId) AND (@SearchId = 0 OR it.Id = @SearchId)

  ----if CatLevel already exists in the table, just use it instead for better performance
  --SELECT it.CatLevel, it.Id, it.ParentId, it.Name FROM ItemTree it 
  --WHERE (@RootId = 0 OR it.RootId = @RootId) AND (@SearchId = 0 OR it.Id = @SearchId)
)
, LeafIds AS --use it to show only leaf categories
(
  SELECT DISTINCT Id FROM ItemTree it WHERE (@RootId = 0 OR it.RootId = @RootId)
  EXCEPT
  SELECT DISTINCT ParentId FROM ItemTree it WHERE (@RootId = 0 OR it.RootId = @RootId)
)
, LeafTree AS --prepare data for pivots
(
  SELECT t.CatLevel, t.Id, t.ParentId, t.Name, Id AS LeafId
  FROM Tree t --WHERE Id IN (SELECT Id FROM LeafIds)

  UNION ALL

  SELECT t.CatLevel, t.Id, t.ParentId, t.Name, lt.LeafId
  FROM Tree t
  INNER JOIN LeafTree lt ON lt.ParentId = t.Id
)
, TreeFinal AS --make pivots for Id and Name over same leafIds
(
  SELECT t.*, pt1.[1] AS ID_1, pt1.[2] AS ID_2, pt1.[3] AS ID_3, pt1.[4] AS ID_4, pt1.[5] AS ID_5
  , pt2.[1] AS Name_1, pt2.[2] AS Name_2, pt2.[3] AS Name_3, pt2.[4] AS Name_4, pt2.[5] AS Name_5
  , ISNULL(pt2.[1], '') + ISNULL(' - ' + pt2.[2], '') + ISNULL(' - ' + pt2.[3], '') + ISNULL(' - ' + pt2.[4], '') + ISNULL(' - ' + pt2.[5], '')  AS Path
  FROM Tree t

  JOIN (
    SELECT LeafId, CatLevel, Id
    FROM LeafTree
  ) source
  PIVOT ( MAX(Id) FOR CatLevel IN ([1], [2], [3], [4], [5]) ) AS pt1 ON pt1.LeafId = t.Id

  JOIN (
    SELECT LeafId, CatLevel, Name
    FROM LeafTree
  ) source
  PIVOT ( MAX(Name) FOR CatLevel IN ([1], [2], [3], [4], [5]) ) AS pt2 ON pt2.LeafId = t.Id
) 
--Use following to update CatLevel in your table, if it was not there before
--UPDATE it SET CatLevel = Tree.CatLevel FROM Tree JOIN dbo.ItemTree it ON it.Id = Tree.Id
SELECT * FROM TreeFinal ORDER BY Path

这是查询前后的屏幕截图

I know the post is quite old, but here is how I did that.

In my table there are several root categories, therefore I use @RootId to specify which one I am after. Root category has ParentID set to NULL.

If you do not have CatLevel column in your categories table - cte Tree creates it for you. If you are going to use the query frequently, it is better to add it to the table (UPDATE code is commented in the end).

If you wish to have dynamic number of columns (not fixed to 5 as in my case), make a dynamic SQL query for it.

DECLARE @RootId INT, @SearchId INT
SET @RootId = 0 
SET @SearchId = 0

--SELECT it.Id, it.ParentId, it.Name, it.RootId FROM ItemTree it 
--WHERE (@RootId = 0 OR it.RootId = @RootId) AND (@SearchId = 0 OR it.Id = @SearchId)

;WITH Tree AS --gets category levels
(
  SELECT 1 as CatLevel, it.Id, it.ParentId, it.Name, it.RootId
  FROM ItemTree it WHERE it.ParentId IS NULL AND (@RootId = 0 OR it.RootId = @RootId) AND (@SearchId = 0 OR it.Id = @SearchId)

  UNION ALL

  SELECT t.CatLevel + 1 as CatLevel, it.Id, it.ParentId, it.Name, it.RootId
  FROM ItemTree it
  INNER JOIN Tree t ON it.ParentId = t.Id
  WHERE (@RootId = 0 OR it.RootId = @RootId) AND (@SearchId = 0 OR it.Id = @SearchId)

  ----if CatLevel already exists in the table, just use it instead for better performance
  --SELECT it.CatLevel, it.Id, it.ParentId, it.Name FROM ItemTree it 
  --WHERE (@RootId = 0 OR it.RootId = @RootId) AND (@SearchId = 0 OR it.Id = @SearchId)
)
, LeafIds AS --use it to show only leaf categories
(
  SELECT DISTINCT Id FROM ItemTree it WHERE (@RootId = 0 OR it.RootId = @RootId)
  EXCEPT
  SELECT DISTINCT ParentId FROM ItemTree it WHERE (@RootId = 0 OR it.RootId = @RootId)
)
, LeafTree AS --prepare data for pivots
(
  SELECT t.CatLevel, t.Id, t.ParentId, t.Name, Id AS LeafId
  FROM Tree t --WHERE Id IN (SELECT Id FROM LeafIds)

  UNION ALL

  SELECT t.CatLevel, t.Id, t.ParentId, t.Name, lt.LeafId
  FROM Tree t
  INNER JOIN LeafTree lt ON lt.ParentId = t.Id
)
, TreeFinal AS --make pivots for Id and Name over same leafIds
(
  SELECT t.*, pt1.[1] AS ID_1, pt1.[2] AS ID_2, pt1.[3] AS ID_3, pt1.[4] AS ID_4, pt1.[5] AS ID_5
  , pt2.[1] AS Name_1, pt2.[2] AS Name_2, pt2.[3] AS Name_3, pt2.[4] AS Name_4, pt2.[5] AS Name_5
  , ISNULL(pt2.[1], '') + ISNULL(' - ' + pt2.[2], '') + ISNULL(' - ' + pt2.[3], '') + ISNULL(' - ' + pt2.[4], '') + ISNULL(' - ' + pt2.[5], '')  AS Path
  FROM Tree t

  JOIN (
    SELECT LeafId, CatLevel, Id
    FROM LeafTree
  ) source
  PIVOT ( MAX(Id) FOR CatLevel IN ([1], [2], [3], [4], [5]) ) AS pt1 ON pt1.LeafId = t.Id

  JOIN (
    SELECT LeafId, CatLevel, Name
    FROM LeafTree
  ) source
  PIVOT ( MAX(Name) FOR CatLevel IN ([1], [2], [3], [4], [5]) ) AS pt2 ON pt2.LeafId = t.Id
) 
--Use following to update CatLevel in your table, if it was not there before
--UPDATE it SET CatLevel = Tree.CatLevel FROM Tree JOIN dbo.ItemTree it ON it.Id = Tree.Id
SELECT * FROM TreeFinal ORDER BY Path

Here's screenshot of the before and after queries

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文