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の結果を挿入する修飾「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 

編集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

@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 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が再帰ごとに再構築されるため、パフォーマンスが低下します。少なくともそれは何が起こっているの私の推測ですが、それは高速ではないと私を信じています。

+0

私のポストを更新したのとほぼ同じロジックです。あなたの時間と助けてくれてありがとう – OzanYukruk

+1

@OzanYukruk - これは**単一のCTEで**最初はルートからダウンしています(無視します)。 –

+0

ええ、あなたの例では、参照として一時テーブルを使用していますが、私の場合、参照テーブルはCTEで得られた結果セットです(実際には1 "以上のUNION ALLを使用しています)コード、親子階層を持つ10個のテーブルをつけて)返される結果セットが必要かどうかを判断する別のパラメータを送信する – OzanYukruk