2

我可以通过使用 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 级是否正确?想了很久,可能会犯一些愚蠢的错误。

4

1 回答 1

4

样本数据

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。至少这是我对正在发生的事情的猜测,但相信我这并不快。

于 2012-04-10T20:02:31.067 回答