【问题标题】:Order records so children appear just after parent in a multi-level hierarchy对记录进行排序,以便子级在多级层次结构中紧跟在父级之后
【发布时间】:2018-01-15 14:32:27
【问题描述】:

我有一个项目表,其中包含多级层次结构的所有记录。所以,有一个顶级项目,它下面有项目,下面有项目,依此类推;此外,此表中还会有许多顶级项目。

我的目标是从这个表中返回记录,这样每个父母后面跟着它的孩子的记录(这些孩子可以按任何顺序排列),然后每个孩子后面跟着它的孩子(这些孩子可以在任何订单)就在它之后,依此类推。我为此创建了一个 SQLFiddle:SQLFiddle for this

我已尝试使用以下查询获取此信息,但第三级层次结构项并未出现在其父项之后。

select Id, ParentId, ChildLevel, 
CASE WHEN ChildLevel = 0 THEN Id 
     WHEN ChildLevel = 1 then Id  
     WHEN ChildLevel >  1 THEN ParentId END Num
from Items order by UltimateParentId, ChildLevel, Num ;

问题

有没有办法让孩子出现在上面的 sql fiddle 中的父级之后?

使用数据创建此表的脚本如下。

create table Items ( Id int , ParentId int , ChildLevel int,     UltimateParentId int);

--I could have multiple hierarchy level for each top level item
--right now I am populating data at multiple levels for only one top level item
--but the table will consist of many top level items having their own hierarchy records

insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (1,0,0,0);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (2,1,1,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (3,1,1,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (4,1,1,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (5,3,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (7,2,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (6,3,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (8,4,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (9,3,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (10,2,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (11,4,2,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (12,11,3,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (13,5,3,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (14,11,3,1);
insert into Items (Id, ParentId, ChildLevel, UltimateParentId) values (15,5,3,1);

【问题讨论】:

    标签: sql sql-server-2008 hierarchical-data


    【解决方案1】:

    由于您没有为 ID/Parent 使用字符串,因此您可以利用 hierarchyid 数据类型。

    R1、R2 和 HierPath 在最终选择中是可选的。 R1 和 R2 表示所有权范围。这有助于聚合和选择,而不必递归。

    示例

    ;with cteP as (
          Select ID
                ,ParentId 
                ,HierID = convert(hierarchyid,concat('/',ID,'/'))
          From   Items 
          Where  ParentId=0
          Union  All
          Select ID  = r.ID
                ,ParentId  = r.ParentId 
                ,HierID = convert(hierarchyid,concat(p.HierID.ToString(),r.ID,'/'))
          From   Items r
          Join   cteP p on r.ParentId  = p.ID)
    Select R1    = Row_Number() over (Order By HierID)
          ,R2    = Row_Number() over (Order By HierID) + -1 + (Select count(*) From cteP where HierID.ToString() like A.HierID.ToString()+'%')
          ,Lvl   = HierID.GetLevel()
          ,ID
          ,ParentId
          ,HierID
          ,HierPath = HierID.GetDescendant ( null , null ).ToString()   
     From cteP A
     Order By A.HierID
    

    退货

    【讨论】:

    • @Sunil 如果您的实际表很大并且性能很关键。从最终选择中删除 R1 和 R2。
    • @Sunil 忘记“完成”R2 计算。查看更新的答案
    • 是的,我的桌子可能很大。正如我在以下 jsfiddle 中验证的那样,它的工作原理:sqlfiddle.com/#!6/d8a4f/1。第二个查询是你给的,而第一个是我的。
    • 移除 R1 和 R2 后,执行时间从 200 毫秒降至 21 毫秒。
    • @Sunil CTE 很棒,但性能可能会因大层次结构而受到影响。只是好奇你的hier有多少行?还有另一种通过 SP 的方法可以在 3 到 4 秒内生成 200K 层
    【解决方案2】:

    根据要求。以下可以在几秒钟内生成 200K hier

    我应该添加...您在哪里看到Row_Number() over (Order By ID)我 使用的 ID,但这可以是任何其他字段来确定所需的 演示顺序。

    示例

    Select *,Lvl=1,Seq=cast(1000000+Row_Number() over (Order By ID) as varchar(900)) Into #TempBld from Items Where  ParentID = 0
    
    Declare @Cnt int=1
    While @Cnt<=30
        Begin
            Insert Into #TempBld 
            Select A.*,Lvl=B.Lvl+1,Seq=B.Seq+' '+ cast(1000000+Row_Number() over (Order By A.ID) as varchar(25))
             From  Items A
             Join  #TempBld B on (B.Lvl=@Cnt and A.ParentID=B.ID)
            Set @Cnt=@Cnt+1
        End
    
    Select ID,R1=cast(Row_Number() over (Order by Seq) as int),Seq Into #TempR1 From #TempBld
    Create Clustered Index idx on #TempR1 (Seq)
    Select A.ID,R2 = Max(B.R1) Into  #TempR2 From  #TempBld A Join #TempR1  B on B.Seq Like A.Seq+'%' Group By A.ID
    
    Select B.R1
          ,C.R2
          ,A.Lvl 
          ,A.ID
          ,A.ParentID
     From  #TempBld A
     Join  #TempR1  B on (A.ID=B.ID)
     Join  #TempR2  C on (A.ID=C.ID)
     Order By 1
    

    退货

    【讨论】:

    • 非常感谢。在这种方法中,30 是层次结构中的级别数,但我可以将其增加到更高的值,例如 100。
    • @Sunil 当然。我的层次结构永远不会超过 20 个级别,但我可以看到对话更深入
    • 我将如何使用它为父母订购孩子?在最初的问题中,我提到孩子的顺序并不重要,但以防万一我想订购孩子,即回复 CreateDate 的评论。
    • @Sunil 在第二个答案中看到我的黄色评论。您可以使用任何其他所需的字段。在第一个答案中,我假设 ID 是一个可以保持序列的身份。
    • @Sunil 标准化序列键 将其视为廉价的零填充。
    【解决方案3】:

    是的。 公共表表达式 (CTE) 可以是递归的。如果您使用 CTE 您只需要父母 ID。查看https://technet.microsoft.com/pl-pl/library/ms186243(v=sql.105).aspx

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-01
      • 2013-07-23
      • 1970-01-01
      • 2016-09-06
      • 1970-01-01
      • 2014-07-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多