https://school.programmers.co.kr/learn/courses/30/lessons/301650
대장균은 분화하며, 분화를 시작한 개체를 부모, 분화되어 나온 개체를 자식이라고 한다.
ECOLI_DATA 테이블에는 대장균 개체의 ID와 부모 ID 등이 저장되어 있고, 최초 개체(루트)는 PARENT_ID가 NULL이다.
| Column name | Type | Nullable |
|---|---|---|
| ID | INTEGER | FALSE |
| PARENT_ID | INTEGER | TRUE |
| SIZE_OF_COLONY | INTEGER | FALSE |
| DIFFERENTIATION_DATE | DATE | FALSE |
| GENOTYPE | INTEGER | FALSE |
3세대 대장균의 ID를 출력하는 SQL을 작성한다.
IDID 오름차순이 문제는 PARENT_ID → ID 관계로 트리(계보)가 만들어지는 구조라서, “세대”는 루트에서부터 내려온 깊이(depth) 로 볼 수 있습니다.
3세대만 구하면 되므로, 재귀를 쓰지 않고 SELF JOIN을 2번 해서
를 만들면 됩니다.
👉 핵심 아이디어 한 줄 요약
PARENT_ID IS NULL인 루트에서 시작해 self join 2번으로 3세대만 뽑습니다.
SELECT g3.ID
FROM ECOLI_DATA g1
JOIN ECOLI_DATA g2 ON g2.PARENT_ID = g1.ID
JOIN ECOLI_DATA g3 ON g3.PARENT_ID = g2.ID
WHERE g1.PARENT_ID IS NULL
ORDER BY g3.ID;
g1: 1세대(부모가 없는 루트, PARENT_ID IS NULL)g2: 2세대(1세대의 자식)g3: 3세대(2세대의 자식)세대가 “3세대”처럼 고정된 값이라면 SELF JOIN이 제일 직관적입니다.
SELECT c.ID
FROM ECOLI_DATA p
JOIN ECOLI_DATA c ON c.PARENT_ID = p.ID
WHERE p.PARENT_ID IS NULL
ORDER BY c.ID;
SELECT g3.ID
FROM ECOLI_DATA g1
JOIN ECOLI_DATA g2 ON g2.PARENT_ID = g1.ID
JOIN ECOLI_DATA g3 ON g3.PARENT_ID = g2.ID
WHERE g1.PARENT_ID IS NULL
ORDER BY g3.ID;
“특정 N세대”처럼 세대(depth)가 바뀔 수 있는 문제라면 재귀 CTE가 유리합니다.
WITH RECURSIVE gens AS (
-- 1세대(루트)
SELECT ID, PARENT_ID, 1 AS gen
FROM ECOLI_DATA
WHERE PARENT_ID IS NULL
UNION ALL
-- 부모(gen) + 1 => 자식 gen
SELECT e.ID, e.PARENT_ID, g.gen + 1
FROM ECOLI_DATA e
JOIN gens g ON e.PARENT_ID = g.ID
)
SELECT *
FROM gens;
WITH RECURSIVE gens AS (
SELECT ID, PARENT_ID, 1 AS gen
FROM ECOLI_DATA
WHERE PARENT_ID IS NULL
UNION ALL
SELECT e.ID, e.PARENT_ID, g.gen + 1
FROM ECOLI_DATA e
JOIN gens g ON e.PARENT_ID = g.ID
)
SELECT ID
FROM gens
WHERE gen = 3
ORDER BY ID;