【问题标题】:TSQL recursive CTE orderTSQL 递归 CTE 顺序
【发布时间】:2016-08-10 18:07:22
【问题描述】:

我无法弄清楚如何使用递归 CTE 对结果进行递归排序。这就是我的意思(这是一个简化的数据集):

我有这个作为输入:

declare @sections table (id int, parent int);

insert into @sections values (1, 1);
insert into @sections values (2, 2);
insert into @sections values (3, 2);
insert into @sections values (4, 2);
insert into @sections values (5, 4);
insert into @sections values (6, 1);
insert into @sections values (7, 6);
insert into @sections values (8, 6);
insert into @sections values (9, 6);
insert into @sections values (10, 9);

-- hierarchical view
--1
--  6
--      7
--      8
--          10
--      9
--2
--  3
--  4
--      5

我想要这个作为输出 编辑:行的顺序是这里的重要部分

--  id  parent  depth
--  1   1       0
--  6   1       1
--  7   6       2
--  8   6       2
-- 10   8       3
--  9   6       2
--  2   2       0

这是我能做的最好的:

with section_cte as
(
    select id, parent, 0 'depth' from @sections where id = parent
    union all
    select cte.id, cte.parent, depth + 1 
    from @sections s join section_cte cte on s.parent = cte.id where s.id <> s.parent
)
select *from section_cte

谁能帮我调整这个查询以获得我需要的东西?

谢谢!

【问题讨论】:

  • 你能解释一下输出吗
  • 你为什么选择根级项目是它们自己的父母的方案?这些父母通常是空的。
  • @MartinSmith 在我们的系统中,根级别是 id = parent。这就是为什么模拟数据是这样的原因
  • @TheGameiswar 输出中的行顺序就像是深度优先递归遍历树一样。

标签: tsql recursion common-table-expression


【解决方案1】:

您错过了需要从 cte 确定深度的部分

WITH CTE AS (
SELECT
    id
    , parent
    , 0 AS depth
FROM
    @sections
WHERE
    id=parent

UNION ALL

SELECT
    s.id
    , s.parent
    , c.depth + 1
FROM
    @sections s
    JOIN CTE c ON s.parent=c.id AND s.id <> s.parent
)
SELECT *
FROM CTE;

【讨论】:

  • 谢谢,这有帮助,但它并不能完全给我想要的结果。这将按部分 ID 1、2、3、...、10 的顺序返回部分。我期望的顺序是 1 6 7 8 10 9 2 3 4 5
  • 在 TSQL 中,保证输出数据集顺序的唯一方法是使用 ORDER BY 子句。在这种情况下,鉴于您所说的,我不知道如何制作。一个用于循环遍历 ID 的光标,但这变成了一个完全不同的问题。
【解决方案2】:

在递归构建期间,添加一个序列是一件小事。在下面的示例中,顺序由标题的字母顺序驱动,但您可以使用任何其他可用的键/序列。

Declare @Table table (ID int,Pt int,Title varchar(50))
Insert into @Table values (0,null,'Tags'),(1,0,'Transportation'),(2,1,'Boats'),(3,1,'Cars'),(4,1,'Planes'),(5,1,'Trains'),(6,0,'Technology'),(7,6,'FTP'),(8,6,'HTTP'),(9,0,'Finance'),(10,9,'FTP'),(11,9,'401K'),(12,2,'Sail'),(13,2,'Powered'),(14,6,'Internet'),(15,6,'Database'),(16,15,'SQL Server'),(17,15,'MySQL'),(18,15,'MS Access')

Declare @Top  int = null          --<<  Sets top of Hier Try 9
Declare @Nest varchar(25) ='   '  --<<  Optional: Added for readability

;with cteHB (Seq,ID,Pt,Lvl,Title) as (
    Select  Seq  = cast(1000+Row_Number() over (Order by Title) as varchar(500))
           ,ID
           ,Pt
           ,Lvl=1
           ,Title 
     From   @Table 
     Where  IsNull(@Top,-1) = case when @Top is null then isnull(Pt,-1) else ID end
     Union  All
     Select Seq  = cast(concat(cteHB.Seq,'.',1000+Row_Number() over (Order by cteCD.Title)) as varchar(500))
           ,cteCD.ID
           ,cteCD.Pt,cteHB.Lvl+1
           ,cteCD.Title 
     From   @Table cteCD 
     Join   cteHB on cteCD.Pt = cteHB.ID)
     ,cteR1 as (Select Seq,ID,R1=Row_Number() over (Order By Seq) From cteHB)
     ,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select Hier='HierName'
      ,B.R1  
      ,C.R2
      ,A.ID
      ,A.Pt
      ,A.Lvl
      ,Title = Replicate(@Nest,A.Lvl) + A.Title
      --,A.Seq               --<< Normally Excluded, but you can see how the sequence is built
 From cteHB A
 Join cteR1 B on A.ID=B.ID
 Join cteR2 C on A.ID=C.ID
 Order By A.Seq              --<< Use R1 if Range Keys are used

返回

Hier        R1  R2  ID  Pt      Lvl    Title
HierName    1   19  0   NULL    1      Tags
HierName    2   4   9   0       2         Finance
HierName    3   3   11  9       3            401K
HierName    4   4   10  9       3            FTP
HierName    5   12  6   0       2         Technology
HierName    6   9   15  6       3            Database
HierName    7   7   18  15      4               MS Access
HierName    8   8   17  15      4               MySQL
HierName    9   9   16  15      4               SQL Server
HierName    10  10  7   6       3            FTP
HierName    11  11  8   6       3            HTTP
HierName    12  12  14  6       3            Internet
HierName    13  19  1   0       2         Transportation
HierName    14  16  2   1       3            Boats
HierName    15  15  13  2       4               Powered
HierName    16  16  12  2       4               Sail
HierName    17  17  3   1       3            Cars
HierName    18  18  4   1       3            Planes
HierName    19  19  5   1       3            Trains

现在,您可能已经注意到 R1 和 R2。这些是我的范围键,通常用于在不递归的情况下聚合数据。如果您不需要或不想要这些,只需删除 cteR1 和 cteR2(以及最终 SELECT 中的相应引用)。

编辑

您还可以选择层次结构的一部分(即技术及其子项)。

【讨论】:

    【解决方案3】:

    您需要的技巧是创建一个符合您的业务规则的排序字符串。像这样的。

    WITH CTE AS (
    SELECT
        id, parent, 0 AS depth
        --For MS SQL Server 2012+
        ,cast(format(id,'0000') as varchar(max)) sort
        --For previous versions
        ,cast(stuff('0000',5-len(cast(id as varchar)),len(cast(id as varchar)),id) as varchar(max)) sort1
    FROM @sections
    WHERE id=parent
    UNION ALL
    SELECT s.id, s.parent, c.depth + 1
        ,sort + cast(format(s.id,'0000') as varchar(max)) sort
        ,sort + cast(stuff('0000',5-len(cast(s.id as varchar)),len(cast(s.id as varchar)),s.id) as varchar(max)) sort1
    FROM @sections s
        inner join CTE c ON s.parent=c.id AND s.id <> s.parent
    )
    SELECT *
    FROM CTE
    order by sort --or by sort1 depending on version
    

    【讨论】:

    • 我接受这个答案,因为它是根据我的数据而言的,但 John Cappelletti 的回答也很有帮助
    猜你喜欢
    • 2012-04-23
    • 2012-12-07
    • 1970-01-01
    • 2010-10-08
    • 1970-01-01
    • 1970-01-01
    • 2011-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多