トップページに戻る    次のSQLパズルへ    前のSQLパズルへ

再帰with句03 Oracleのorder siblings byを模倣

SQLパズル

siblingsT
ID  OyaID
--  -----
 1   null
 2      1
 3      1
 4      1
 5      3
 6      3
 7      4
 8      4
 9      6
10      7
20   null
21     20
22     20
23     21
24     21

Oracleの下記のクエリと同じ結果を取得する。

select connect_by_root ID as treeID,
ID,OyaID,Level,
sys_connect_by_path(to_char(ID),',') as path
  from siblingsT
start with OyaID is null
connect by prior ID = OyaID
order siblings by ID desc;

出力結果
treeID  ID  OyaID  Level  path
------  --  -----  -----  ---------
    20  20   null      1  ,20
    20  22     20      2  ,20,22
    20  21     20      2  ,20,21
    20  24     21      3  ,20,21,24
    20  23     21      3  ,20,21,23
     1   1   null      1  ,1
     1   4      1      2  ,1,4
     1   8      4      3  ,1,4,8
     1   7      4      3  ,1,4,7
     1  10      7      4  ,1,4,7,10
     1   3      1      2  ,1,3
     1   6      3      3  ,1,3,6
     1   9      6      4  ,1,3,6,9
     1   5      3      3  ,1,3,5
     1   2      1      2  ,1,2


データ作成スクリプト

create table siblingsT(
ID    integer not null primary key,
OyaID integer);

insert into siblingsT values
( 1,null),
( 2,   1),
( 3,   1),
( 4,   1),
( 5,   3),
( 6,   3),
( 7,   4),
( 8,   4),
( 9,   6),
(10,   7),
(20,null),
(21,  20),
(22,  20),
(23,  21),
(24,  21);


SQL

with recursive RowN(ID,OyaID,sk) as(
select ID,OyaID,Row_Number() over(order by ID desc)
  from siblingsT),
w(ID,OyaID,arrID,sk) as(
select ID,OyaID,array[ID],array[sk]
  from RowN
 where OyaID is null
union all
select b.ID,b.OyaID,w.arrID || b.ID,
w.sk || b.sk
  from w,RowN b
 where w.ID = b.OyaID)
select arrID[1] as treeID,ID,OyaID,
array_upper(arrID,1) as Level,
',' || array_to_string(arrID,',') as path,sk
  from w
order by sk;


解説

根からの経路でソートしてます。

配列型のソートの仕組み
9.17. 配列関数と演算子

PGCon2009: Introduction to Recursive QueriesのPDFの19ページに、
Simulate Oracle-style ORDER SIBLINGS BY using an array of ROW()s containing the sort key.
と書かれてますね。

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
ちなみに、複数キーを持ったテーブルの場合は、
dense_rank関数で一時的なサロゲートキーを作成して配列型にセットしていくといいでしょう。
SQLアタマアカデミー:サロゲートキーVSナチュラルキー