1

SQL Server의 With 명령 (CTE)을 사용하여 재귀를 수행 할 수 있습니다.재귀 순서 (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의 결과를 삽입이 조금 수정 (I는 WHERE T.ID = (@ FROM MAX (ID)를 선택 "알고 TMP)

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 

편집 :이 오류가있을 수 있습니다

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

주는, 메신저하지 않도록 아직 , 단지 쌍 (3,1)이 레벨 2와 맞는지 확인하기 위해 보여주고 싶습니까? 꽤 오랫동안 이것을 생각해 봤는데, 어리석은 실수를 저지를 수도 있습니다. 루트에서

+0

@MartinSmith 작동하지 않는 게시자는 신중하게 게시물을 읽습니다. – JonH

+0

@MartinSmith는 역순으로 계층 구조를 다시 만듭니다. – JonH

+0

먼저 계층 구조를 만들어야하므로 기본적으로 기존 CTE 뒤에 다른 CTE를 추가하여 뒤로 이동해야합니다. 트리를 먼저 구축하지 않고 얼마나 많은 레벨이 깊은지를 알 수있는 방법이 없습니다. – JNK

답변

4

샘플 데이터

declare @T table 
(
    ParentID int, 
    ID int 
) 

insert into @T values 
(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 
:

;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 

재귀

결과 : 당신이 많은 지점이있는 경우 함께 병합으로

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는 각 재귀마다 다시 작성되기 때문에 성능이 저하됩니다. 적어도 그것은 일어나는 일에 대한 나의 추측이지만 그것이 빠르지 않다는 것을 믿습니다.

+0

예, 게시물을 업데이트 한 것과 거의 같은 논리입니다. 처음에는 단일 CTE로이 문제를 처리하고 매개 변수를 사용하여 재귀 순서를 결정하십시오. 시간과 도움을 주셔서 감사합니다 – OzanYukruk

+1

@OzanYukruk -이 **는 단일 CTE를 사용합니다 ** 첫 번째는 루트와 다운 (무시)에서 두 번째는 그렇지 않은 노드를 찾습니다. 부모님과 걸어서. –

+0

그래,하지만 당신의 예제에서는 임시 테이블을 참조로 사용하고 있습니다.하지만 제 경우에는 참조 테이블이 CTE로 얻어진 결과 세트입니다. (실제 "UNION ALL" 코드, 상위 - 하위 계층 구조를 가진 10 개의 테이블을 연결) 따라서 다른 매개 변수를 보내어 되돌릴 결과 집합이 필요한지 결정해야합니다. – OzanYukruk