계층적 구조를 다루는 것은 관계형 데이터베이스의 고전입니다. 예: 디렉토리, 트리형 메뉴, 조직 구조.
문제의 역사: 데이터베이스는 테이블 모델입니다. 트리형 데이터에 대해서는 몇 가지 접근 방식이 있으며, 각각 장단점이 있습니다.
문제: 표준 모델인 parent_id는 느린 재귀 SELECT를 초래합니다. 실제 작업(모든 자식 검색, 경로 찾기, 서브트리 계산)은 최적화를 요구합니다.
해결책:
물리 경로를 위한 예제 코드 (PostgreSQL):
CREATE TABLE categories ( id SERIAL PRIMARY KEY, name TEXT, path TEXT ); -- 경로 저장 예: '1/2/5/' (루트/서브카테고리/현재) SELECT * FROM categories WHERE path LIKE '1/2/%'; -- 2의 모든 자식
네스트 세트를 위한 예제 코드:
CREATE TABLE nested_categories ( id SERIAL PRIMARY KEY, name TEXT, lft INT NOT NULL, rgt INT NOT NULL ); -- 자식 노드 SELECT * FROM nested_categories WHERE lft > 2 AND rgt < 15;
주요 특징:
모든 자식을 찾기 위해 parent_id로 중첩 SELECT를 단순히 사용할 수 있나요?
이것은 작은 깊이에서만 작동합니다. 재귀 검색을 위해서는 재귀 CTE (WITH RECURSIVE) 또는 더 복잡한 스키마가 필요하며, 단순 JOIN은 상당한 호출 수와 성능 저하로 이어집니다.
예:
WITH RECURSIVE cte AS ( SELECT id, parent_id, name FROM categories WHERE id = 1 UNION ALL SELECT c.id, c.parent_id, c.name FROM categories c INNER JOIN cte ON c.parent_id = cte.id ) SELECT * FROM cte;
왜 분자 트리(네스트 세트)는 서브트리를 빠르게 추출하지만 노드를 추가/삭제하는 것은 느리나요?
트리 구조를 변경할 때 여러 행의 lft/rgt를 동시에 변경해야 합니다 (변경 단계 — 삽입/삭제 보다 큰 모든 값). 읽기에는 이상적이지만 자주 변경되는 경우 다른 방법을 사용하세요.
언제 클로저 테이블을 사용하고, parent_id 또는 물리 경로를 사용하지 않나요?
클로저 테이블은 모든 레벨의 자식에 대해 반복적인 쿼리에 잘 작동하며 관계를 정기적으로 재계산하는 데 적합합니다. 단점은 더 많은 공간을 요구합니다.
예:
CREATE TABLE closure ( ancestor INT, descendant INT, depth INT );
인터넷 쇼핑몰에서 카테고리는 parent_id로 구현되었습니다. 모든 하위 항목은 수동으로 설정되며, 하위 카테고리 검색은 중첩 SELECT로 수행됩니다.
장점:
단점:
물리 경로 또는 클로저 테이블이 사용됩니다. 모든 하위 카테고리에 대한 쿼리는 즉각적이며, 재계산은 그룹 스크립트로 수행됩니다.
장점:
단점: