재귀 CTE는 트리 구조나 계층형 데이터를 다룰 때 매우 유용한 기능이다. 특히 부모-자식 관계 데이터를 탐색할 때 많이 사용한다.
CTE (Common Table Expression)
WITH CTE_NAME AS (
SELECT ...
)
SELECT * FROM CTE_NAME;
- 쿼리 안에서 임시로 사용하는 테이블
- 가독성을 높이고, 서브쿼리를 반복하지 않을 수 있게 한다.
재귀 CTE (Recursive Common Table Expression)
재귀 CTE는 자기 자신을 참조하는 CTE이다. 한 번 재귀 CTE를 실행하면, 내부적으로 반복(루프)을 통해 레코드를 점진적으로 확장한다.
구조
재귀 CTE는 두 부분으로 나누어진다:
- Anchor Member (기저 멤버)
- 재귀가 시작되는 출발점
- 보통 PARENT_ID IS NULL처럼, 트리 구조의 루트 노드를 찾는 쿼리이다.
- Recursive Member (재귀 멤버)
- Anchor의 결과를 입력으로 받아 자기 자신을 반복적으로 호출
- JOIN을 통해 부모와 자식 간의 관계를 이어가는 쿼리이다.
이 둘은 UNION ALL을 통해 묶인다.
WITH RECURSIVE CTE_NAME AS (
-- ① Anchor Member
SELECT ... FROM 테이블 WHERE 조건
UNION ALL
-- ② Recursive Member
SELECT ... FROM 테이블
JOIN CTE_NAME ON 연결 조건
)
SELECT * FROM CTE_NAME;
내부 동작 원리
재귀 CTE는 내부적으로 다음과 같은 방식으로 작동한다.
- Anchor Member 쿼리가 실행되어 초기 결과 세트 생성 (Generation 1)
- Recursive Member가 Anchor 결과와 JOIN되어 다음 세대 생성
- 새로 생성된 결과를 다시 Recursive Member에 투입하여 JOIN 반복
- 더 이상 결과가 나오지 않을 때까지 반복 (종료 조건 만족)
즉, CTE_NAME은 마치 누적되는 임시 테이블처럼 동작하며, 세대가 하나씩 늘어나는 방식으로 데이터가 전개된다.
UNION ALL을 써야 하는 이유
UNION은 중복 제거를 수행하기 때문에 성능 저하와 불필요한 오버헤드가 발생할 수 있다. 재귀 CTE에서는 일반적으로 중복을 신경 쓰지 않으므로 UNION ALL을 사용하는 것이 좋다.
재귀 종료 조건
재귀 CTE는 암묵적으로 더 이상 JOIN될 데이터가 없을 때 종료된다.
하지만 실수로 종료 조건이 누락되거나 루프 구조가 잘못되면, 무한 루프나 성능 저하가 발생할 수 있기 때문에, 명시적인 종료 조건을 재귀 멤버 안에 넣는 것이 좋다.
- 예시
WHERE GENERATION <= 5 -- 최대 5세대까지만 조회
728x90
'SQL' 카테고리의 다른 글
[SQL] 비트 연산 (0) | 2025.04.01 |
---|---|
[SQL] SQL 정규 표현식 (SQL Regular Expression) (0) | 2025.01.12 |
[SQL] 가독성 높이기 (0) | 2025.01.08 |
[SQL] INSTR() (0) | 2024.12.19 |
[SQL] SUBSTR() (0) | 2024.12.19 |