메뉴 여닫기
개인 메뉴 토글
로그인하지 않음
만약 지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.
편집 요약 없음
 
(같은 사용자의 중간 판 4개는 보이지 않습니다)
21번째 줄: 21번째 줄:


예:
예:
 
<source lang=sql>
SELECT employee_id, manager_id, LEVEL
SELECT employee_id, manager_id, LEVEL
   FROM employees
   FROM employees
27번째 줄: 27번째 줄:
CONNECT BY PRIOR employee_id = manager_id
CONNECT BY PRIOR employee_id = manager_id
ORDER SIBLINGS BY employee_id;
ORDER SIBLINGS BY employee_id;
</source>


=== PRIOR 키워드 설명 ===
=== PRIOR 키워드 설명 ===
방식 설명
방식 설명
<source lang=sql>
CONNECT BY PRIOR 자식 = 부모 부모 → 자식 (Top-down)
CONNECT BY PRIOR 자식 = 부모 부모 → 자식 (Top-down)
</source>
<source lang=sql>
CONNECT BY 부모 = PRIOR 자식 자식 → 부모 (Bottom-up)
CONNECT BY 부모 = PRIOR 자식 자식 → 부모 (Bottom-up)
</source>


예제:
예제:


-- 순방향 전개
-- 순방향 전개
<source lang=sql>
CONNECT BY PRIOR empno = mgr
CONNECT BY PRIOR empno = mgr
</source>
-- 역방향 전개
-- 역방향 전개
<source lang=sql>
CONNECT BY empno = PRIOR mgr
CONNECT BY empno = PRIOR mgr
</source>


=== LEVEL 가상 컬럼 ===
=== LEVEL 가상 컬럼 ===
45번째 줄: 55번째 줄:
=== CONNECT_BY_ROOT 함수 ===
=== CONNECT_BY_ROOT 함수 ===
현재 행의 루트(시작점) 값을 반환합니다.
현재 행의 루트(시작점) 값을 반환합니다.
 
<source lang=sql>
SELECT LEVEL, ename, CONNECT_BY_ROOT ename AS root_ename
SELECT LEVEL, ename, CONNECT_BY_ROOT ename AS root_ename
   FROM emp
   FROM emp
  START WITH mgr IS NULL
  START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr;
CONNECT BY PRIOR empno = mgr;
</source>


=== CONNECT_BY_ISCYCLE 가상 컬럼 ===
=== CONNECT_BY_ISCYCLE 가상 컬럼 ===
사이클(순환 참조)을 감지할 수 있는 가상 컬럼입니다.
사이클(순환 참조)을 감지할 수 있는 가상 컬럼입니다.
NOCYCLE 옵션과 함께 사용해야 하며, 순환이 감지되면 1을, 아니면 0을 반환합니다.
NOCYCLE 옵션과 함께 사용해야 하며, 순환이 감지되면 1을, 아니면 0을 반환합니다.
 
<source lang=sql>
SELECT empno, CONNECT_BY_ISCYCLE, LEVEL
SELECT empno, CONNECT_BY_ISCYCLE, LEVEL
   FROM emp
   FROM emp
  START WITH mgr IS NULL
  START WITH mgr IS NULL
CONNECT BY NOCYCLE PRIOR empno = mgr;
CONNECT BY NOCYCLE PRIOR empno = mgr;
</source>


=== CONNECT_BY_ISLEAF 가상 컬럼 ===
=== CONNECT_BY_ISLEAF 가상 컬럼 ===
해당 노드가 리프(leaf, 자식이 없는 노드)인지를 판별합니다.
해당 노드가 리프(leaf, 자식이 없는 노드)인지를 판별합니다.
 
<source lang=sql>
SELECT empno, CONNECT_BY_ISLEAF
SELECT empno, CONNECT_BY_ISLEAF
   FROM emp
   FROM emp
  START WITH mgr IS NULL
  START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr;
CONNECT BY PRIOR empno = mgr;
</source>


=== SYS_CONNECT_BY_PATH 함수 ===
=== SYS_CONNECT_BY_PATH 함수 ===
루트부터 현재 행까지의 경로를 구분자와 함께 문자열로 반환합니다.
루트부터 현재 행까지의 경로를 구분자와 함께 문자열로 반환합니다.
 
<source lang=sql>
SELECT empno,
SELECT empno,
       SYS_CONNECT_BY_PATH(ename, ' > ') AS path
       SYS_CONNECT_BY_PATH(ename, ' > ') AS path
76번째 줄: 89번째 줄:
  START WITH mgr IS NULL
  START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr;
CONNECT BY PRIOR empno = mgr;
</source>


=== 성능 팁 ===
=== 성능 팁 ===
86번째 줄: 100번째 줄:


예:
예:
 
<source lang=sql>
SELECT /*+ LEADING(A) USE_MERGE(B) */
SELECT /*+ LEADING(A) USE_MERGE(B) */
       COUNT (*)
       COUNT (*)
94번째 줄: 108번째 줄:
       CONNECT BY LEVEL <= 100) b
       CONNECT BY LEVEL <= 100) b
  WHERE b.lv <= a.c1;
  WHERE b.lv <= a.c1;
</source>


== 실전 예제: 테스트 테이블 생성 및 계층 쿼리 ==
== 실전 예제: 테스트 테이블 생성 및 계층 쿼리 ==
<source lang=sql>


CREATE TABLE t1 (
CREATE TABLE t1 (
109번째 줄: 125번째 줄:
INSERT INTO t1 VALUES ('e','f');
INSERT INTO t1 VALUES ('e','f');
COMMIT;
COMMIT;
</source>


순방향 계층 전개 (부모 → 자식):
순방향 계층 전개 (부모 → 자식):
 
<source lang=sql>
SELECT parent_c, child_c, LEVEL
SELECT parent_c, child_c, LEVEL
   FROM t1
   FROM t1
  START WITH parent_c = 'a'
  START WITH parent_c = 'a'
CONNECT BY PRIOR child_c = parent_c;
CONNECT BY PRIOR child_c = parent_c;
</source>


역방향 계층 전개 (자식 → 부모):
역방향 계층 전개 (자식 → 부모):
 
<source lang=sql>
SELECT parent_c, child_c, LEVEL
SELECT parent_c, child_c, LEVEL
   FROM t1
   FROM t1
  START WITH child_c = 'f'
  START WITH child_c = 'f'
CONNECT BY child_c = PRIOR parent_c;
CONNECT BY child_c = PRIOR parent_c;
</source>


== 날짜 생성 활용 예제 ==
== 날짜 생성 활용 예제 ==


1) 일자 리스트 구하기:
1) 일자 리스트 구하기:
 
<source lang=sql>
SELECT TO_CHAR(TO_DATE('20240101','YYYYMMDD') + LEVEL - 1, 'YYYY-MM-DD') AS day
SELECT TO_CHAR(TO_DATE('20240101','YYYYMMDD') + LEVEL - 1, 'YYYY-MM-DD') AS day
   FROM dual
   FROM dual
CONNECT BY LEVEL <= TO_DATE('20240110','YYYYMMDD') - TO_DATE('20240101','YYYYMMDD') + 1;
CONNECT BY LEVEL <= TO_DATE('20240110','YYYYMMDD') - TO_DATE('20240101','YYYYMMDD') + 1;
</source>


2) 월별 리스트 구하기:
2) 월별 리스트 구하기:
 
<source lang=sql>
SELECT TO_CHAR(ADD_MONTHS(TO_DATE('202201','YYYYMM'), LEVEL-1), 'YYYY-MM') AS month
SELECT TO_CHAR(ADD_MONTHS(TO_DATE('202201','YYYYMM'), LEVEL-1), 'YYYY-MM') AS month
   FROM dual
   FROM dual
CONNECT BY LEVEL <= 12;
CONNECT BY LEVEL <= 12;
</source>


== 결론 ==
== 결론 ==
CONNECT BY는 단순한 계층 쿼리 기능을 넘어, 조직도, 트리구조, BOM, 기간 생성 등 다양한 용도로 사용되는 필수 SQL 기능입니다. 위에서 다룬 가상 컬럼과 성능 팁을 익히면 보다 효율적으로 데이터 구조를 탐색할 수 있습니다.
* CONNECT BY는 단순한 계층 쿼리 기능을 넘어, 조직도, 트리구조, BOM, 기간 생성 등 다양한 용도로 사용되는 필수 SQL 기능입니다.  
* 위에서 다룬 가상 컬럼과 성능 팁을 익히면 보다 효율적으로 데이터 구조를 탐색할 수 있습니다.


[[Category:Oracle]] [[Category:SQL]] [[Category:계층형쿼리]] [[Category:오라클 성능 팁]]
[[Category:Oracle]] [[Category:SQL]] [[Category:계층형쿼리]] [[Category:오라클 성능 팁]]

2025년 5월 22일 (목) 21:26 기준 최신판

Oracle CONNECT BY 계층형 쿼리

오라클(Oracle) 데이터베이스에서 계층형 데이터를 조회할 때 CONNECT BY 절을 사용합니다. 트리 구조, 조직도, BOM 구조 등의 데이터를 SQL 하나로 탐색하고 출력할 수 있는 강력한 기능입니다.

이 글에서는 CONNECT BY, START WITH, LEVEL, CONNECT_BY_ROOT, CONNECT_BY_ISCYCLE, CONNECT_BY_ISLEAF, SYS_CONNECT_BY_PATH 등 계층형 SQL에서 사용하는 핵심 요소들을 예제와 함께 정리합니다.

CONNECT BY 절 구조

설명
START WITH 계층 구조의 시작점(루트 노드)을 지정
CONNECT BY 부모-자식 간 연결 조건 정의
PRIOR 연결 방향 지정 (부모 → 자식 또는 자식 → 부모)
ORDER SIBLINGS BY 같은 부모를 가진 노드 간 정렬


예:

SELECT employee_id, manager_id, LEVEL
  FROM employees
 START WITH manager_id IS NULL
CONNECT BY PRIOR employee_id = manager_id
ORDER SIBLINGS BY employee_id;

PRIOR 키워드 설명

방식 설명

CONNECT BY PRIOR 자식 = 부모	부모 → 자식 (Top-down)
CONNECT BY 부모 = PRIOR 자식	자식 → 부모 (Bottom-up)

예제:

-- 순방향 전개

CONNECT BY PRIOR empno = mgr

-- 역방향 전개

CONNECT BY empno = PRIOR mgr

LEVEL 가상 컬럼

LEVEL은 계층의 깊이를 나타내는 가상 컬럼입니다. 루트 노드는 1부터 시작하며, 자식으로 갈수록 2, 3, ...으로 증가합니다.

CONNECT_BY_ROOT 함수

현재 행의 루트(시작점) 값을 반환합니다.

SELECT LEVEL, ename, CONNECT_BY_ROOT ename AS root_ename
  FROM emp
 START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr;

CONNECT_BY_ISCYCLE 가상 컬럼

사이클(순환 참조)을 감지할 수 있는 가상 컬럼입니다. NOCYCLE 옵션과 함께 사용해야 하며, 순환이 감지되면 1을, 아니면 0을 반환합니다.

SELECT empno, CONNECT_BY_ISCYCLE, LEVEL
  FROM emp
 START WITH mgr IS NULL
CONNECT BY NOCYCLE PRIOR empno = mgr;

CONNECT_BY_ISLEAF 가상 컬럼

해당 노드가 리프(leaf, 자식이 없는 노드)인지를 판별합니다.

SELECT empno, CONNECT_BY_ISLEAF
  FROM emp
 START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr;

SYS_CONNECT_BY_PATH 함수

루트부터 현재 행까지의 경로를 구분자와 함께 문자열로 반환합니다.

SELECT empno,
       SYS_CONNECT_BY_PATH(ename, ' > ') AS path
  FROM emp
 START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr;

성능 팁

   START WITH 조건 컬럼에는 인덱스가 존재해야 성능 저하를 방지할 수 있습니다.
   LEVEL이 깊어질수록 비용이 증가합니다. 필요하다면 LEVEL <= n 조건을 함께 사용하세요.
   DUAL CONNECT BY 형태는 조인 없이 다량의 반복 데이터를 생성할 수 있으나, 성능 이슈 발생 시 MERGE JOIN을 고려하세요.

예:

SELECT /*+ LEADING(A) USE_MERGE(B) */
       COUNT (*)
  FROM t1 a
     , (SELECT LEVEL lv 
          FROM DUAL 
       CONNECT BY LEVEL <= 100) b
 WHERE b.lv <= a.c1;

실전 예제: 테스트 테이블 생성 및 계층 쿼리


CREATE TABLE t1 (
  parent_c VARCHAR2(1),
  child_c VARCHAR2(1)
);

INSERT INTO t1 VALUES ('a','b');
INSERT INTO t1 VALUES ('b','c');
INSERT INTO t1 VALUES ('a','c');
INSERT INTO t1 VALUES ('c','d');
INSERT INTO t1 VALUES ('c','e');
INSERT INTO t1 VALUES ('e','f');
COMMIT;

순방향 계층 전개 (부모 → 자식):

SELECT parent_c, child_c, LEVEL
  FROM t1
 START WITH parent_c = 'a'
CONNECT BY PRIOR child_c = parent_c;

역방향 계층 전개 (자식 → 부모):

SELECT parent_c, child_c, LEVEL
  FROM t1
 START WITH child_c = 'f'
CONNECT BY child_c = PRIOR parent_c;

날짜 생성 활용 예제

1) 일자 리스트 구하기:

SELECT TO_CHAR(TO_DATE('20240101','YYYYMMDD') + LEVEL - 1, 'YYYY-MM-DD') AS day
  FROM dual
CONNECT BY LEVEL <= TO_DATE('20240110','YYYYMMDD') - TO_DATE('20240101','YYYYMMDD') + 1;

2) 월별 리스트 구하기:

SELECT TO_CHAR(ADD_MONTHS(TO_DATE('202201','YYYYMM'), LEVEL-1), 'YYYY-MM') AS month
  FROM dual
CONNECT BY LEVEL <= 12;

결론

  • CONNECT BY는 단순한 계층 쿼리 기능을 넘어, 조직도, 트리구조, BOM, 기간 생성 등 다양한 용도로 사용되는 필수 SQL 기능입니다.
  • 위에서 다룬 가상 컬럼과 성능 팁을 익히면 보다 효율적으로 데이터 구조를 탐색할 수 있습니다.