【问题标题】:SQL Server Tree Hierarchy and Nested Sets with Duplicate Record idsSQL Server 树层次结构和具有重复记录 id 的嵌套集
【发布时间】:2009-07-14 10:56:57
【问题描述】:

鉴于我有这个结果集结构(多余的字段已被剥离)

Id | ParentId | Name | Depth
----------------------------

是否可以按树顺序返回记录,即Parent 然后Children,如果ChildParent,那么它们的Children,如果不是那么Sibling,等等?例如,

Id | ParentId | Name | Depth
----------------------------
1    NULL       Major    1
2    1          Minor    2
3    1          Minor    2
4    3          Build    3
5    3          Build    3
6    1          Minor    2

/* etc, etc */

我能想到的唯一方法就是关注这篇文章 -

Improve hierarchy performance using nested sets

并在每条记录中包含[LeftExtent][RightExtent] 字段。现在文章中的 SQL 在Ids 是唯一的情况下工作正常,但是在这个特定的树结构中,具有相同Id 的记录可以出现在树中的不同位置(ParentId 字段显然不同)。我认为问题出在文章中的这个 SQL 中 -

  INSERT INTO @tmpStack
    (
      EmployeeID, 
      LeftExtent
    )
  SELECT TOP 1 EmployeeID, @counter 
  FROM Employee 
  WHERE ISNULL(ParentID, 0) = ISNULL(@parentid,0) 
  /* If the Id has already been added then record is not given [LeftExtent] or [RightExtent] values. */
  AND EmployeeID NOT IN (SELECT EmployeeID FROM @tmpStack) 

如何更改此设置以允许为具有重复 Ids 的记录提供 [LeftExtent] 和 [RightExtent] 值,或者我完全错过了按我需要的顺序返回结果集的更简单方法?

【问题讨论】:

    标签: sql sql-server sql-server-2005 tree nested-sets


    【解决方案1】:

    这是一个对我有用的方法:

    @ParentID 只是层次结构中的一个起点,但你可以传入 0(但我认为你使用 null 作为基本 ID,所以你会明白的)

    有序排序的关键在于构建的排序键。

    WITH RoleHierarchy (RoleID, [Role], [Description], ParentID, Editable, HierarchyLevel, SortKey) AS
    (
       -- Base
       SELECT
            RoleID,
            [Role],
            [Description],
            ParentID,
            Editable,
            0 as HierarchyLevel,
            CAST(RoleID AS VARBINARY(300))
       FROM
            dbo.Roles       
       WHERE
            RoleID = @ParentID
    
       UNION ALL
    
       -- Recursive
       SELECT
            e.RoleID,
            e.[Role],
            e.[Description],
            e.ParentID,
            e.Editable,
            th.HierarchyLevel + 1 AS HierarchyLevel,
            CAST (th.SortKey + CAST (e.[Role] AS VARBINARY(100)) + CAST (e.[RoleID] AS VARBINARY(100)) AS VARBINARY(300))
       FROM
            Roles e
            INNER JOIN RoleHierarchy th ON e.ParentID = th.RoleID
        WHERE
            e.RoleID != 0
    )
    
    SELECT
        RoleID,
        ParentID,
        [Role],
        [Description],
        Editable,
        HierarchyLevel
    FROM
        RoleHierarchy
    WHERE
        RoleID != @ParentID
    ORDER BY
        SortKey
    

    【讨论】:

    • 好吧,如果您发现什么奇怪的东西,请告诉我,因为我已经使用了一段时间了!
    • 您的解决方案效果很好,我使用 seq(序列)列而不是主键并且它有效。我还在这里找到了更详细的排序键解释sqlservercentral.com/articles/Development/…
    【解决方案2】:

    您应该看看 SQL Server 2005 中的递归公用表表达式:

    在你的情况下,这将是这样的:

    WITH EmployeeCTE AS
    (
       -- get the anchor
       SELECT ID, ParentID, Name, 0 as 'Depth'
       FROM Employee WHERE ParentID IS NULL
    
       -- recursively union lower levels
       UNION ALL
       SELECT e.ID, e.ParentID, e.Name, e.Depth+1
       FROM Employee e
       INNER JOIN EmployeeCTE ON e.ParentID = EmployeeCTE.ID
    )
    SELECT * FROM EmployeeCTE
    

    这应该会为您提供一个很好的查询结果集,其中包含您正在寻找的数据。还是我错过了什么?

    马克

    【讨论】:

    • 我目前有一个 CTE 可以递归地加入记录以构建结果集。这很好用,但问题是结果集中的记录顺序不是我想要的问题中指定的顺序。我会将问题修改为状态结果集结构而不是表结构
    【解决方案3】:

    如果你使用materialized pathHIERARCHYID,你的生活会轻松很多...

    【讨论】:

    • HIERARCHYID 看起来很有趣,但是,解决方案是使用 SQL Server 2005
    • 物化路径在 2005 年确实有效,它可以让你的生活更轻松
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-15
    • 1970-01-01
    • 2010-12-01
    • 1970-01-01
    相关资源
    最近更新 更多