Sql server 递归顺序(SQL Server CTE)

Sql server 递归顺序(SQL Server CTE),sql-server,recursion,common-table-expression,with-statement,Sql Server,Recursion,Common Table Expression,With Statement,我可以通过使用SQL Server的命令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.

我可以通过使用SQL Server的命令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结果插入临时表,然后使用另一个递归将顺序颠倒,因为我知道T.ID=SELECT MAXID 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级?考虑这个问题已经有一段时间了,我可能会犯一些愚蠢的错误。

示例数据

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
ParentID    ID          level
----------- ----------- -----------
NULL        3           1
NULL        4           0
2           1           2
3           1           2
4           2           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
----------- -----------
NULL        3
NULL        4
4           2
2           1
3           1
ParentID    ID          level
----------- ----------- -----------
NULL        3           1
NULL        4           0
2           1           2
3           1           2
4           2           1
用多CTE查询替换@T2是可能的,但这是一个非常糟糕的主意。它会破坏性能,因为每次递归都会重建第一个CTE。至少这是我对正在发生的事情的猜测,但相信我,这并不快

样本数据

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
ParentID    ID          level
----------- ----------- -----------
NULL        3           1
NULL        4           0
2           1           2
3           1           2
4           2           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
----------- -----------
NULL        3
NULL        4
4           2
2           1
3           1
ParentID    ID          level
----------- ----------- -----------
NULL        3           1
NULL        4           0
2           1           2
3           1           2
4           2           1


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

@MartinSmith这行不通仔细阅读文章。@MartinSmith以相反的顺序重新创建层次结构。您需要首先构建层次结构,因此基本上在现有CTE之后添加另一个CTE才能向后。如果不先建立一棵树,就无法知道某事物有多少层深。也许包括提供你的样本结果的样本数据将有助于那些试图帮助你的人。@OzanYukruk-清楚地定义他们想要什么。这个问题很难思考,更不用说回答了。然后以一种最终没有意义的方式回答。@MartinSmith,这不起作用,请仔细阅读文章。@MartinSmith以相反的顺序重新创建层次结构。您需要先构建层次结构,因此基本上在现有CTE之后添加另一个CTE以使其倒退。如果不先建立一棵树,就无法知道某事物有多少层深。也许包括提供你的样本结果的样本数据将有助于那些试图帮助你的人。@OzanYukruk-清楚地定义他们想要什么。这个问题很难思考,更不用说回答了。然后用一种最终毫无意义的方式来回答。是的,这和我更新帖子时的逻辑差不多。起初,我试图用一个CTE来处理这个问题,并使用一个参数来确定递归的顺序。谢谢你的时间和帮助。@OzanYukruk-这是一台CTE。第一个是从根开始,然后忽略它。第二个查找不是父节点的节点并向上移动。是的,但在您的示例中,您使用临时表作为引用。然而,在我的例子中,我的参考表是使用CTE获得的结果集。在我的实际代码中使用多于1个UNION ALL,附加10个具有父子层次结构的表,因此我必须发送另一个参数来确定它是否也需要反向结果集。@OzanYukruk Yes。temp表不是你拥有的任何信息来源。是的,在我的情况下,这是我的第一个CTE:。是的,与我更新帖子的逻辑基本相同。起初,我试图用一个CTE来处理这个问题,并使用一个参数来确定递归的顺序。谢谢你的时间和帮助。@OzanYukruk-这是一台CTE。第一个是从根开始,然后忽略它。第二个查找不是父节点的节点并向上移动。是的,但在您的示例中,您使用临时表作为引用。然而,在我的例子中,我的参考表是使用CTE获得的结果集。在我的真实代码中使用超过1个联合,比如10
表具有父子层次结构,因此我必须发送另一个参数来确定它是否也需要反向结果集。@OzanYukruk是。temp表不是您拥有的任何信息源。是的,在我的情况下,这是我的第一个CTE:。