PostgreSQL 18의 재귀 CTE CYCLE node SET is_cycle USING path는 경로에서 반복 방문한 node를 발견한 결과 행에 is_cycle = true를 표시하고 경로 열도 만듭니다. 순환 표시 행은 결과에서 확인할 수 있지만 그 행을 기준으로 재귀 처리를 더 이어가지는 않습니다.
따라서 A→B→C→A를 추적하면 마지막 A가 순환 행으로 표시되고, A→B로 다시 확장해 무한 반복하지 않습니다. CYCLE은 노드 열을 무엇으로 추적할지 지정해야 의미가 분명합니다.
CYCLE은 재방문 행을 표시하고 그 뒤의 확장을 멈춥니다
재귀 CTE는 기준 행에서 시작해 재귀 항을 반복 적용하면서 결과를 만듭니다. 그래프 간선이 A→B, B→C, C→A처럼 순환하면 종료 조건이 없는 질의는 다시 A에 도착하고 탐색을 계속할 수 있습니다. 출력 행 전체가 똑같지 않은 경우에는 UNION으로 중복 행을 지우는 것만으로 순환을 없애지 못할 수도 있습니다. 깊이 같은 값이 달라져 매번 새로운 행으로 보일 수 있기 때문입니다.
PostgreSQL 18은 재귀 CTE에서 CYCLE 절로 방문 여부를 추적할 열, 순환 표시 열, 경로 열을 지정할 수 있습니다. 이전 경로에 이미 등장한 node가 다시 발견되면 그 행에 순환 표시가 붙습니다. 이 표시는 그 행을 결과에서 식별하게 해주며, 순환을 발견한 뒤 재귀 처리를 중단하도록 합니다. 출력에서 마지막 반복 노드를 숨긴다고 이해하지 말고, 표시된 행과 그 행 이후 확장이 없다는 두 사실을 함께 추적하세요.
| 단계 | node | 이전 경로 | is_cycle | path | 다음 확장 |
|---|---|---|---|---|---|
| 기준 행 | A | 없음 | false | {A} | B |
| 재귀 1 | B | {A} | false | {A,B} | C |
| 재귀 2 | C | {A,B} | false | {A,B,C} | A |
| 재귀 3 | A | {A,B,C} | true | {A,B,C,A} | 중단 |
WITH RECURSIVE walk(node, depth) AS (
VALUES ('A'::text, 0)
UNION ALL
SELECT edge.dst, walk.depth + 1
FROM edge
JOIN walk ON edge.src = walk.node
) CYCLE node SET is_cycle USING path
SELECT node, depth, is_cycle, path
FROM walk;코드에서 CYCLE node는 현재 경로를 따라가며 node 값이 반복되는지 확인하겠다는 뜻입니다. SET is_cycle은 결과에 표시할 순환 표식 열의 이름이고, USING path는 경로를 담을 열입니다. 표의 마지막 행은 이미 경로에 있었던 A를 다시 만난 행입니다. PostgreSQL의 문법은 이 행을 cycle로 표시하고 추가 재귀 처리를 중단합니다.
코드의 edge는 출발 노드와 도착 노드를 담은 교육용 관계를 뜻합니다. 예를 들어 세 행 (A,B), (B,C), (C,A)를 놓고 손으로 따라가면 표의 네 결과 행을 얻습니다. 여기서 수치는 데이터베이스에서 관측한 실행 출력이 아니라 공식 CYCLE 규칙과 이 가상 그래프를 적용한 계산입니다.
추적할 키를 정하고 표시 행에서 멈춥니다
- 재귀 CTE의 기준 행을 적고 현재 경로를 시작합니다.
- 재귀 항을 한 번 적용해 다음 노드나 행을 계산합니다.
- 문제의 순환 기준이 되는 열을 확인합니다. 여기서는 node입니다.
- 새 값이 현재 경로에 이미 있으면 해당 결과 행을 순환으로 표시합니다.
- PostgreSQL CYCLE 규칙에 따라 그 표시 행 이후에는 재귀 확장을 이어가지 않습니다.
- 최종 답에 일반 방문 행과 순환 표시 행의 개수 및 경로를 구분해 씁니다.
순환 검사에 id 대신 다른 열을 쓰면 결과가 달라질 수 있습니다. 예를 들어 조직도에서 사원 id가 아니라 부서명을 추적하면 서로 다른 사람이라도 같은 부서명이 경로에 되풀이될 때 순환처럼 표시할 수 있습니다. 따라서 CYCLE의 추적 열은 질문이 정의한 노드의 식별자와 맞아야 합니다. 두 열의 조합이 노드를 구분한다면 PostgreSQL 문법에서 여러 열을 추적 대상으로 지정할 수 있습니다.
UNION으로 바꾸면 모든 순환이 자동으로 사라진다고 보지 않습니다
흔한 실수는 재귀 쿼리의 UNION ALL을 UNION으로 바꾸기만 하면 어떤 그래프 순환도 끝난다고 생각하는 것입니다. 중복 제거는 완전히 같은 출력 행을 버립니다. 깊이, 경로, 누적 수치가 매번 달라지면 같은 노드를 방문해도 행 전체는 다를 수 있어 중복으로 제거되지 않습니다. PostgreSQL 문서도 일부 순환에서는 특정 열을 경로별로 확인해야 한다고 설명합니다.
또 다른 실수는 마지막 반복 A가 표시되었으므로 그 행 자체가 결과에 없다고 쓰는 것입니다. CYCLE은 cycle mark를 결과 열로 추가합니다. 예제에서는 그 표시 행이 결과에 들어가고, 그 다음 재귀 처리가 이어지지 않습니다. 이 구분이 “무슨 행이 표시되나?”라는 질문의 정답입니다. PostgreSQL 18 문서 기준으로 설명했으며, 이 SQL은 실제 서버에서 실행하지 않았습니다.
정답을 가리고 확인해 보세요
이 글을 위해 만든 학습 예시이며 공식 기출문제나 판매 카드의 인용이 아닙니다.
PostgreSQL 18에서 X→Y→X를 CYCLE node SET is_cycle USING path로 탐색하면 어느 행이 표시되고 다음 재귀 단계는 어떻게 되나요?
정답과 해설 확인
첫 X와 Y는 일반 행입니다. 경로에 있던 X를 다시 만난 마지막 행이 is_cycle=true로 표시되며, 그 cycle 행 뒤의 재귀 확장은 중단됩니다.PostgreSQL 18 WITH Queries와 SELECT 문서는 CYCLE이 지정 열의 재방문을 표시하고 경로 열을 추가하며 cycle 발견 후 재귀 union 처리를 멈춘다고 설명합니다.