재귀적 CTE 구조 (SQL Server 2005 온라인 설명서)
의사 코드 및 의미 체계
재귀적 CTE 구조에는 앵커 멤버와 재귀 멤버가 적어도 하나씩 포함되어야 합니다. 다음 의사 코드에서는 단일 앵커 멤버와 단일 재귀 멤버가 포함된 간단한 재귀적 CTE의 구성 요소를 보여 줍니다.
WITH cte_name ( column_name [,...n] )
AS
(
CTE_query_definition –- Anchor member is
defined.
UNION ALL
CTE_query_definition –- Recursive member is
defined referencing cte_name.
)
-- Statement using the CTE
SELECT *
FROM cte_name
재귀 실행의 의미 체계는 다음과 같습니다.
- CTE 식을 앵커
멤버와 재귀 멤버로 분할합니다.
- 앵커 멤버를 실행하여 첫 번째 호출 또는 기본 결과 집합(T0)
을 만듭니다.
- Ti는 입력으로 사용하고 Ti+1은 출력으로 사용하여 재귀 멤버를 실행합니다.
- 빈 집합이 반환될 때까지 3단계를 반복합니다.
- 결과 집합을 반환합니다. 이것은 T0에서 Tn까지의 UNION ALL입니다.
--------------------------------------------------------------------------------------------
예)
create table test_rcte
(
idx int,
p_idx int,
val varchar(10)
);
insert into test_rcte(idx, p_idx, val) values (1, null, 'root');
insert into test_rcte(idx, p_idx, val) values (2, 1, 'user1');
insert into test_rcte(idx, p_idx, val) values (3, 1, 'user2');
insert into test_rcte(idx, p_idx, val) values (4, 1, 'user3');
insert into test_rcte(idx, p_idx, val) values (5, 2, 'user4');
insert into test_rcte(idx, p_idx, val) values (6, 2, 'user5');
insert into test_rcte(idx, p_idx, val) values (7, 3, 'user6');
insert into test_rcte(idx, p_idx, val) values (8, 3, 'user7');
insert into test_rcte(idx, p_idx, val) values (9, 4, 'user8');
insert into test_rcte(idx, p_idx, val) values (10, 5, 'user9');
insert into test_rcte(idx, p_idx, val) values (11, 5, 'user10');
insert into test_rcte(idx, p_idx, val) values (12, 6, 'user11');
insert into test_rcte(idx, p_idx, val) values (13, 6, 'user12');
insert into test_rcte(idx, p_idx, val) values (14, 7, 'user14');
insert into test_rcte(idx, p_idx, val) values (15, 7, 'user15');
insert into test_rcte(idx, p_idx, val) values (16, 8, 'user16');
insert into test_rcte(idx, p_idx, val) values (17, 8, 'user17');
insert into test_rcte(idx, p_idx, val) values (18, 10, 'user18');
MSSQL | val | lvl | ORACLE | val | lvl | |
root | 1 | root | 1 | |||
user1 | 2 | user1 | 2 | |||
with cte (val, p_idx, idx, lvl) | user2 | 2 | select val, level as lvl | user3 | 2 | |
as | user3 | 2 | from test_rcte | user2 | 2 | |
( | user8 | 3 | start with p_idx is null | user6 | 3 | |
select val, p_idx, idx, 1 as lvl | user6 | 3 | connect by prior idx = p_idx | user4 | 3 | |
from test_rcte | user7 | 3 | order by level; | user7 | 3 | |
where p_idx is null | user4 | 3 | user8 | 3 | ||
union all | user5 | 3 | user5 | 3 | ||
select a.val, a.p_idx, a.idx, lvl + 1 | user11 | 4 | user17 | 4 | ||
from test_rcte a, cte b | user12 | 4 | user16 | 4 | ||
where a.p_idx = b.idx | user9 | 4 | user14 | 4 | ||
) | user10 | 4 | user15 | 4 | ||
select val, lvl from cte | user16 | 4 | user12 | 4 | ||
order by lvl | user17 | 4 | user11 | 4 | ||
user14 | 4 | user9 | 4 | |||
user15 | 4 | user10 | 4 | |||
user18 | 5 | user18 | 5 |
MSSQL | val | lvl | path |
root | 1 | / root | |
with cte (val, p_idx, idx, lvl, path) | user1 | 2 | / root / user1 |
as | user2 | 2 | / root / user2 |
( | user3 | 2 | / root / user3 |
select val, p_idx, idx, 1 as lvl, convert(varchar(100), ' / ' + val) path | user8 | 3 | / root / user3 / user8 |
from test_rcte | user6 | 3 | / root / user2 / user6 |
where p_idx is null | user7 | 3 | / root / user2 / user7 |
union all | user4 | 3 | / root / user1 / user4 |
select a.val, a.p_idx, a.idx, lvl + 1, convert(varchar(100), path + ' / ' + a.val) | user5 | 3 | / root / user1 / user5 |
from test_rcte a, cte b | user11 | 4 | / root / user1 / user5 / user11 |
where a.p_idx = b.idx | user12 | 4 | / root / user1 / user5 / user12 |
) | user9 | 4 | / root / user1 / user4 / user9 |
select val, lvl, path from cte | user10 | 4 | / root / user1 / user4 / user10 |
order by lvl | user16 | 4 | / root / user2 / user7 / user16 |
user17 | 4 | / root / user2 / user7 / user17 | |
user14 | 4 | / root / user2 / user6 / user14 | |
user15 | 4 | / root / user2 / user6 / user15 | |
user18 | 5 | / root / user1 / user4 / user9 / user18 | |
ORACLE | val | lvl | path |
root | 1 | / root | |
select val, level as lvl, sys_connect_by_path(val,' / ') path | user1 | 2 | / root / user1 |
from test_rcte | user3 | 2 | / root / user3 |
start with p_idx is null | user2 | 2 | / root / user2 |
connect by prior idx = p_idx | user6 | 3 | / root / user2 / user6 |
order by level; | user4 | 3 | / root / user1 / user4 |
user7 | 3 | / root / user2 / user7 | |
user8 | 3 | / root / user3 / user8 | |
user5 | 3 | / root / user1 / user5 | |
user17 | 4 | / root / user2 / user7 / user17 | |
user16 | 4 | / root / user2 / user7 / user16 | |
user14 | 4 | / root / user2 / user6 / user14 | |
user15 | 4 | / root / user2 / user6 / user15 | |
user12 | 4 | / root / user1 / user5 / user12 | |
user11 | 4 | / root / user1 / user5 / user11 | |
user9 | 4 | / root / user1 / user4 / user9 | |
user10 | 4 | / root / user1 / user4 / user10 | |
user18 | 5 | / root / user1 / user4 |
출처 : http://blog.jhfrk.net/339
여기서 재귀 호출이 100번 이상일 때 자동적으로 에러 메세지를 던진다.
이 때, 맨 아래에 OPTION (MAXRECURSION 0) 를 추가해주면 무한 반복한다.
0이 아닌 다른 숫자를 했을 때는 해당 횟수만큼 재귀호출을 한다.
이로써, 트리구조는 해결되었다 -_-;
오랜 숙변이 내려가는거 같네.
'DB > MS-SQL' 카테고리의 다른 글
RUN_MEMBER() 함수의 PARTITION 옵션 사용 (0) | 2010.09.30 |
---|---|
CTE 사용시 앵커부분과 재귀 부분 간의 유형이 일치하지 않다고 할 떄 (463) | 2010.07.20 |
MS-SQL Convert (0) | 2010.07.19 |
마지막으로 Insert 된 Ident 값 얻기 (0) | 2010.07.08 |
MS-SQL, Oracle DB Link | (0) | 2010.06.30 |