我可以使用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级是否正确?我现在已经考虑了很长一段时间,我可能会犯一些愚蠢的错误。
答案 0 :(得分:4)
示例数据
declare @T table
(
ParentID int,
ID int
)
insert into @T values
(NULL, 1),
(1 , 2),
(1 , 3),
(2 , 4)
来自root的递归:
;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
可以解决这个问题。
要获得正确的水平,您需要先从上到下计算水平。将其存储在表变量(或临时表)中,然后将其用作leaf-> root递归的源。
-- 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。至少那是我对正在发生的事情的猜测,但相信我并不快。