데이터베이스
[MySQL] 재귀 쿼리 (Recursive Query)
영범
2024. 4. 22. 17:46
재귀 쿼리
재귀 쿼리는 자기 자신을 참조하여 반복적으로 실행되는 쿼리를 말합니다.
이러한 재귀 쿼리는 일반적으로 계층적인 데이터 구조를 다룰 때 사용합니다.
(예: 조직도, 카테고리, 댓글 등)
특징
주요 특징으로는 자기 참조, 반복적 실행, 종료 조건이 있습니다.
- 자기 참조
재귀 쿼리는 쿼리 내에서 자기 자신을 참조합니다. - 반복적 실행
재귀 쿼리는 초기 단계에서 실행하여 반복적으로 실행됩니다.
초기 단계에서는 시작점을 정의하지만, 재귀적으로 실행되는 단계에서 이전의 결과를 기반으로 다음 결과를 계산합니다. - 종료 조건
무한 루프를 방지하기 위해 종료 조건이 필요합니다.
종료 조건은 재귀적으로 실행되는 쿼리의 기저 조건을 정의합니다.
재귀 쿼리의 예시
조직도로 예시를 들어보겠습니다.
CREATE TABLE Employees (
id INT PRIMARY KEY,
name VARCHAR(100),
manager_id INT
);
-- 최상위 관리자
INSERT INTO Employees (id, name, manager_id) VALUES (1, 'John', NULL);
INSERT INTO Employees (id, name, manager_id) VALUES (10, 'Michael', NULL);
-- John의 부하 직원
INSERT INTO Employees (id, name, manager_id) VALUES (2, 'Alice', 1);
INSERT INTO Employees (id, name, manager_id) VALUES (3, 'Bob', 1);
INSERT INTO Employees (id, name, manager_id) VALUES (4, 'Charlie', 2);
INSERT INTO Employees (id, name, manager_id) VALUES (5, 'David', 3);
INSERT INTO Employees (id, name, manager_id) VALUES (6, 'Eva', 2);
INSERT INTO Employees (id, name, manager_id) VALUES (7, 'Frank', 3);
INSERT INTO Employees (id, name, manager_id) VALUES (8, 'Grace', 6);
INSERT INTO Employees (id, name, manager_id) VALUES (9, 'Henry', 6);
-- Michael의 부하 직원
INSERT INTO Employees (id, name, manager_id) VALUES (11, 'Oliver', 10);
INSERT INTO Employees (id, name, manager_id) VALUES (12, 'Patricia', 10);
INSERT INTO Employees (id, name, manager_id) VALUES (13, 'Quinn', 10);
조직도 내에서 위와 같은 데이터가 있다고 해봅시다.
여기서 John이 직접/간접적으로 관리하는 직원의 목록을 보고 싶다면 아래와 같이 재귀 쿼리를 만들면 됩니다.
WITH RECURSIVE Subordinates AS (
SELECT id, name, manager_id, 1 as depth
FROM Employees
WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.manager_id, s.depth + 1
FROM Employees e
JOIN Subordinates s ON e.manager_id = s.id -- 부하 직원의 부하 직원을 재귀적으로 찾습니다.
WHERE s.depth < 3 -- 깊이가 3보다 작을 때만 재귀를 수행합니다.
)
SELECT * FROM Subordinates WHERE id != 1; -- 'John'을 제외한 결과만 선택합니다.
- WITH RECURSIVE Subordinates
재귀적인 Common Table Expression(CTE)를 정의하는 부분입니다.
이름을 'Subordiantes'로 정의하고 아래에서 재귀적으로 참조됩니다. - SELECT id, name, manager_id, 1 as depth FROM Employees WHERE id = 1
초기 쿼리로 재귀의 시작점이 되며 첫 번째 루프에서 실행됩니다.
추가로 출력을 원하는 깊이를 설정하기 위해서 depts 컬럼을 추가했습니다. - SELECT e.id, e.name, e.manager_id FROM Employees e JOIN Subordinates s ON e.manager_id = s.id WHERE s.depth < 3
재귀 쿼리로 이전 단계에서 선택된 직원 부하들의 부하 직원을 선택합니다.
이를 통해 부하 직원이 관리하는 직원을 재귀적으로 찾습니다.
종료 조건을 추가해 깊이가 3보다 작을 때까지만 재귀를 수행하도록 합니다. - SELECT * FROM Subordinates WHERE id != 1
John은 제외해서 결과를 조회합니다.
결과를 순서대로 조회
우선 초기 단계에서는 John만 조회됩니다.
id | name | manager_id | depth |
1 | John | NULL | 1 |
첫 번째 재귀 단계에서는 John의 직접적인 부하직원이 조회됩니다.
id | name | manager_id | depth |
1 | John | NULL | 1 |
2 | Alice | 1 | 2 |
3 | Bob | 1 | 2 |
두 번째 재귀 단계에서는 Alice와 Bob의 부하직원이 조회됩니다.
id | name | manager_id | depth |
1 | John | NULL | 1 |
2 | Alice | 1 | 2 |
3 | Bob | 1 | 2 |
4 | Charlie | 2 | 3 |
5 | David | 3 | 3 |
6 | Eva | 2 | 3 |
7 | Frank | 3 | 3 |
이 이후부터는 Depth가 3 이상이므로 재귀 쿼리가 종료됩니다.