【问题标题】:Order of Recursion (SQL Server CTE)递归顺序 (SQL Server CTE)
【发布时间】:2012-04-23 02:44:31
【问题描述】:

我可以通过使用 SQL Server 的 With 命令 (CTE) 来实现递归。

WITH MyCTE(ParentID,ID,Name,Level)
AS
(
SELECT ManagerID AS ParentID, UserID AS ID, UserName AS Name, 0 AS Level 
FROM USERS U       
WHERE U.ManagerID IS NULL

UNION ALL

SELECT U.ManagerID AS ParentID, U.UserID AS ID, U.UserName AS Name, H.Level+1 AS Level
FROM USERS U
INNER JOIN MyCTE H ON H.ID = U.ManagerID
)

SELECT ParentID,ID FROM MyCTE

返回

ParentID    ID
NULL        1
1           2
1           3
2           4

我想要实现的是反转这个结果集。即把根节点和最深的子节点反转为,

ParentID    ID
NULL        4
4           2
2           1
3           1

无法弄清楚如何以编程方式实现这一点(最好使用 CTE),例如使用参数来确定递归顺序等。非常感谢任何帮助,谢谢。

编辑:

修改了这一点,将我的第一个 CTE 的结果插入到临时表中,然后使用另一个递归我将顺序反转为(我知道“WHERE T.ID = (SELECT MAX(ID) FROM @tmp)”不会在实际情况,我还得用“Level”列确定最深的节点,只是为了简化这个例子),

 INSERT INTO @tmp
 SELECT ParentID,ID,Level FROM MyCTE
 WITH MyCTE2(ParentID,ID,Level)
 AS
 (
 SELECT NULL AS ParentID, ID AS ID, 0 AS Level FROM @tmp T 
 WHERE T.ID = (SELECT MAX(ID) FROM @tmp)

 UNION ALL

 SELECT R2.ID AS ParentID, T.ParentID AS ID, R2.Level+1 FROM @tmp T
 INNER JOIN MyCTE2 R2 ON R2.ID = T.ID
 WHERE T.ParentID IS NOT NULL
 )

原始结果(删除了 1,3 对)

ParentID   ID   Level
  NULL      1     0
    1       2     1
    2       4     2

反转结果,

ParentID   ID   Level
  NULL      4     0
    4       2     1
    2       1     2

编辑 2:

我做了这样的事情,

SELECT TTT.ParentID,TTT.ID,TTT.Level FROM
(
SELECT ParentID,ID,Level FROM MyCTE2
UNION ALL
SELECT TT.ID AS ParentID,TT.ParentID AS ID,(SELECT Level+1 FROM @tmp WHERE ID=TT.ID) 
AS  Level  FROM
(
SELECT ID FROM @tmp
EXCEPT
SELECT ID FROM MyCTE2
)T INNER JOIN @tmp TT ON TT.ID = T.ID
)TTT
ORDER BY TTT.Level

给予,

ParentID    ID  Level
NULL        4   0
4           2   1
2           1   2
3           1   2

这可能包含错误,我还不确定,只是想显示以确保 (3,1) 对与级别 2 是否正确?想了很久,可能会犯一些愚蠢的错误。

【问题讨论】:

  • @MartinSmith 这不起作用仔细阅读帖子。
  • @MartinSmith 以相反的顺序重新创建层次结构。
  • 您需要先构建层次结构,因此基本上在您现有的 CTE 之后添加另一个 CTE 以向后退。如果不先构建树,就无法知道某事物的深度。
  • 也许包括提供你的样本结果的样本数据会帮助那些试图帮助你的人。
  • @OzanYukruk - 明确定义他们想要什么。这个问题很难思考,更别说回答了。并以最终没有意义的方式回答它。

标签: sql-server recursion common-table-expression with-statement


【解决方案1】:

样本数据

declare @T table
(
  ParentID int,
  ID int
)

insert into @T values
(NULL,        1),
(1   ,        2),
(1   ,        3),
(2   ,        4)

从根递归:

;with C as
(
  select ParentID, ID
  from @T
  where ParentID is null
  union all
  select T.ParentID, T.ID
  from @T as T
    inner join C
      on T.ParentID = C.ID
)
select *
from C

结果

ParentID    ID
----------- -----------
NULL        1
1           2
1           3
2           4

从叶子递归:

;with C as
(
  select null as PParentID, ID, ParentID
  from @T
  where ID not in (select ParentID 
                   from @T 
                   where ParentID is not null)
  union all
  select C.ID, T.ID, T.ParentID 
  from @T as T
    inner join C
      on T.ID = C.ParentID 
)
select distinct
       PParentID as ParentID,
       ID
from C

结果:

ParentID    ID
----------- -----------
NULL        3
NULL        4
4           2
2           1
3           1

如果您有许多分支,您将有重复的行合并在一起。使用distinct 可以解决这个问题。

要获得正确的关卡,您需要首先从上到下计算关卡。将其存储在表变量(或临时表)中,然后将其用作叶->根递归的源。

-- Primary key and unique is in there to get the indexes used in the recursion  
declare @T2 table
(
  ParentID int,
  ID int,
  Level int,
  primary key (ID),
  unique(ParentID, ID)
)

;with C as
(
  select ParentID, ID, 0 as Level
  from @T
  where ParentID is null
  union all
  select T.ParentID, T.ID, Level + 1
  from @T as T
    inner join C
      on T.ParentID = C.ID
)
insert into @T2
select ParentID, ID, Level
from C

;with C as
(
  select null as PParentID, ID, ParentID, Level
  from @T2
  where ID not in (select ParentID 
                   from @T2 
                   where ParentID is not null)
  union all
  select C.ID, T.ID, T.ParentID, T.Level
  from @T2 as T
    inner join C
      on T.ID = C.ParentID 
)
select distinct
       PParentID as ParentID,
       ID,
       max(Level) over() - Level as level
from C

结果:

ParentID    ID          level
----------- ----------- -----------
NULL        3           1
NULL        4           0
2           1           2
3           1           2
4           2           1

用多 CTE 查询替换 @T2 是可能的,但这是一个非常糟糕的主意。它会降低性能,因为每次递归都会重建第一个 CTE。至少这是我对正在发生的事情的猜测,但相信我这并不快。

【讨论】:

  • 是的,与我更新帖子的逻辑几乎相同。起初,我试图用一个 CTE 来处理这个问题,并使用一个参数来确定递归的顺序。感谢您的时间和帮助。
  • @OzanYukruk - 这只有一个 CTE。第一个是从根向下(忽略它)。第二个找到不是父节点的节点并向上走。
  • 是的,但在您的示例中,您使用临时表作为参考。但是在我的情况下,我的参考表是使用 CTE 获得的结果集。 (在我的真实代码中使用超过 1 个“UNION ALL”,附加 10 个具有父子层次结构的表)所以我必须发送另一个参数来确定它是否也需要反转的结果集。
  • @OzanYukruk 是的。临时表代替您拥有的任何信息来源。
  • 是的,就我而言,那是我的第一个 CTE :)。
猜你喜欢
  • 1970-01-01
  • 2014-05-19
  • 2010-10-08
  • 2012-12-25
  • 1970-01-01
  • 1970-01-01
  • 2020-06-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多