EMP 의 MGR 열에는 관리자의 EMPNO 가 들어 있다. 같은 테이블 안에서 행이 다른 행을 가리키니, 따라가면 조직도가 된다.
KING (7839, MGR 없음)├─ BLAKE (7698) ── ALLEN · JAMES · MARTIN · TURNER · WARD├─ CLARK (7782) ── MILLER└─ JONES (7566) ─┬ FORD (7902) ── SMITH └ SCOTT (7788) ── ADAMS
이 구조를 한 번의 SELECT 로 위에서 아래로 훑는 문법이 계층형 질의다. Oracle 전용 문법이다.
내일 볼 셀프 조인은 한 단계씩만 잇는다. 계층형 질의는 깊이와 관계없이 끝까지 따라간다.
KING 에서 출발해 부하 쪽으로 내려간다. PRIOR EMPNO = MGR 은 "직전 행의 EMPNO 를 MGR 로 가진 행이 다음"이라는 뜻이다.
SELECT LEVEL, RPAD(LPAD(' ', 2 * (LEVEL - 1)) || ENAME, 16) AS TREE, EMPNO, MGR FROM EMP START WITH MGR IS NULL CONNECT BY PRIOR EMPNO = MGR ORDER SIBLINGS BY ENAME;
LEVEL TREE EMPNO MGR---------- ---------------------------------------------------------------- ---------- ---------- 1 KING 7839 2 BLAKE 7698 7839 3 ALLEN 7499 7698 3 JAMES 7900 7698 3 MARTIN 7654 7698 3 TURNER 7844 7698 3 WARD 7521 7698 2 CLARK 7782 7839 3 MILLER 7934 7782 2 JONES 7566 7839 3 FORD 7902 7566 4 SMITH 7369 7902 3 SCOTT 7788 7566 4 ADAMS 7876 7788
LPAD(' ', 2 * (LEVEL - 1)) 는 LEVEL 1 에서 0칸, 2 에서 2칸, 3 에서 4칸의 공백을 만든다. 이름 앞에 붙이면 들여쓰기가 된다.
바깥 RPAD(…, 16) 은 결과 열의 폭을 줄이려고 붙였다. 빼도 내용은 같다. TREE 열의 긴 구분선은 SQL*Plus 가 열의 최대 바이트 수만큼 그린 것이다.
결과는 깊이 우선으로 나온다. BLAKE 의 부하를 다 보여 준 뒤에야 CLARK 으로 넘어간다.
각 행의 MGR 이 바로 위쪽 부모 행의 EMPNO 와 같다. 그것이 PRIOR EMPNO = MGR 이 만든 연결이다.
LEVEL
사원 수
사원
1
1
KING
2
3
BLAKE, CLARK, JONES
3
8
ALLEN, JAMES, MARTIN, TURNER, WARD, MILLER, FORD, SCOTT
4
2
SMITH, ADAMS
GROUP BY LEVEL 로 세어도 1, 3, 8, 2 가 나온다. 가장 깊은 LEVEL 은 4 다.
루트를 바꾸면 그 아래만 본다. JONES 의 조직이다.
SELECT LEVEL, RPAD(LPAD(' ', 2 * (LEVEL - 1)) || ENAME, 16) AS TREE FROM EMP START WITH ENAME = 'JONES' CONNECT BY PRIOR EMPNO = MGR ORDER SIBLINGS BY ENAME;
LEVEL TREE---------- ---------------------------------------------------------------- 1 JONES 2 FORD 3 SMITH 2 SCOTT 3 ADAMS
LEVEL 은 테이블 전체 기준이 아니라 START WITH 로 정한 루트 기준이다. JONES 가 1 이 된다.
SELECT ENAME, CONNECT_BY_ROOT ENAME AS ROOT, CONNECT_BY_ISLEAF AS LEAF, SUBSTR(SYS_CONNECT_BY_PATH(ENAME, '/'), 1, 25) AS PATH FROM EMP START WITH MGR IS NULL CONNECT BY PRIOR EMPNO = MGR ORDER SIBLINGS BY ENAME;
ENAME ROOT LEAF PATH---------- ---------- ---------- ----------------------------------------------------------------------------------------------------KING KING 0 /KINGBLAKE KING 0 /KING/BLAKEALLEN KING 1 /KING/BLAKE/ALLENJAMES KING 1 /KING/BLAKE/JAMESMARTIN KING 1 /KING/BLAKE/MARTINTURNER KING 1 /KING/BLAKE/TURNERWARD KING 1 /KING/BLAKE/WARDCLARK KING 0 /KING/CLARKMILLER KING 1 /KING/CLARK/MILLERJONES KING 0 /KING/JONESFORD KING 0 /KING/JONES/FORDSMITH KING 1 /KING/JONES/FORD/SMITHSCOTT KING 0 /KING/JONES/SCOTTADAMS KING 1 /KING/JONES/SCOTT/ADAMS
경로는 구분자로 시작한다. /KING/JONES/SCOTT/ADAMS 이지 KING/JONES/… 가 아니다.
SUBSTR 는 열 폭을 줄이려고 붙였다. 경로 내용은 그대로다.
LEAF 가 1 인 사원은 8명이다. 부하가 없는 사원이다.
루트가 여럿이면 CONNECT_BY_ROOT 가 쓸모 있다. 관리자(MANAGER) 셋을 루트로 두고 잎 노드만 남긴다.
SELECT ENAME, CONNECT_BY_ROOT ENAME AS ROOT FROM EMP WHERE CONNECT_BY_ISLEAF = 1 START WITH JOB = 'MANAGER' CONNECT BY PRIOR EMPNO = MGR ORDER SIBLINGS BY ENAME;
SELECT LEVEL, RPAD(LPAD(' ', 2 * (LEVEL - 1)) || ENAME, 16) AS TREE FROM EMP WHERE ENAME <> 'JONES' START WITH MGR IS NULL CONNECT BY PRIOR EMPNO = MGR ORDER SIBLINGS BY ENAME;
LEVEL TREE---------- ---------------------------------------------------------------- 1 KING 2 BLAKE 3 ALLEN 3 JAMES 3 MARTIN 3 TURNER 3 WARD 2 CLARK 3 MILLER 3 FORD 4 SMITH 3 SCOTT 4 ADAMS
이번에는 CONNECT BY 에 AND 로 붙인다.
SELECT LEVEL, RPAD(LPAD(' ', 2 * (LEVEL - 1)) || ENAME, 16) AS TREE FROM EMP START WITH MGR IS NULL CONNECT BY PRIOR EMPNO = MGR AND ENAME <> 'JONES' ORDER SIBLINGS BY ENAME;
LEVEL TREE---------- ---------------------------------------------------------------- 1 KING 2 BLAKE 3 ALLEN 3 JAMES 3 MARTIN 3 TURNER 3 WARD 2 CLARK 3 MILLER
조건 위치
처리 시점
JONES 의 부하 FORD·SCOTT·SMITH·ADAMS
결과 행 수
WHERE
트리를 다 만든 뒤
남는다 (LEVEL 도 그대로 3, 4)
13
CONNECT BY
트리를 만드는 중
JONES 로 내려가는 길이 끊겨 함께 빠진다
9
WHERE 는 결과 거르기다. JONES 한 행만 화면에서 빠지고, 그 부하의 LEVEL 은 바뀌지 않는다.
CONNECT BY 의 조건은 가지치기다. JONES 로 이어지지 않으니 그 아래 가지 전체가 사라진다.
LEVEL <= 2 처럼 깊이를 제한할 때도 같다. WHERE 에 두면 끝까지 만든 뒤 거르고, CONNECT BY 에 두면 3단계로 내려가지 않는다. 결과는 둘 다 KING·BLAKE·CLARK·JONES 4행이다.
계층형 질의 : START WITH … CONNECT BY 로 한 테이블 안의 부모-자식 관계를 따라가며 행을 뽑는 Oracle 문법.
루트 노드 : 트리의 출발점. START WITH 조건에 맞는 행이고 LEVEL 이 1 이다.
잎 노드(leaf) : 자식이 없는 행. CONNECT_BY_ISLEAF 가 1 이다.
PRIOR : CONNECT BY 에서 "직전에 찾은 행의" 열을 가리키는 연산자. 붙은 위치가 방향을 정한다.
순방향 / 역방향 : 부모에서 자식으로 내려가는 전개 / 자식에서 부모로 올라가는 전개.
LEVEL : 계층형 질의에서 루트를 1 로 하는 깊이 값을 주는 의사 열.
ORDER SIBLINGS BY : 트리 모양을 유지하고 같은 부모의 형제끼리만 정렬하는 절.
가지치기 : CONNECT BY 조건으로 특정 행과 그 아래 가지를 트리에서 빼는 것. WHERE 의 결과 거르기와 다르다.
📝 연습 문제
선택지를 클릭하면 정답·해설이 펼쳐집니다.
문제 1
SELECT LEVEL, ENAME FROM EMP START WITH ENAME = 'SMITH' CONNECT BY PRIOR MGR = EMPNO; 에서 LEVEL 이 3 인 사원은? (SMITH 의 관리자는 FORD, FORD 의 관리자는 JONES, JONES 의 관리자는 KING 이다)
문제 2
SELECT ENAME FROM EMP START WITH MGR IS NULL CONNECT BY PRIOR EMPNO = MGR AND ENAME <> 'JONES'; 의 결과 행 수는? (JONES 아래에는 FORD·SCOTT·SMITH·ADAMS 4명이 있고, EMP 는 14명이다)
문제 3
SELECT COUNT(*) FROM EMP WHERE CONNECT_BY_ISLEAF = 1 START WITH MGR IS NULL CONNECT BY PRIOR EMPNO = MGR; 의 결과는? (부하가 있는 사원은 KING·BLAKE·CLARK·JONES·FORD·SCOTT 6명이다)
문제 4
계층형 질의의 ORDER SIBLINGS BY ENAME 에 대한 설명으로 옳은 것은?
문제 5
START WITH MGR IS NULL CONNECT BY PRIOR EMPNO = MGR 로 전개할 때 ADAMS 행의 SYS_CONNECT_BY_PATH(ENAME, '/') 값은? (ADAMS 의 관리자는 SCOTT, SCOTT 의 관리자는 JONES, JONES 의 관리자는 KING 이다)