⚡Пара подходов к описанию деревьев в реляционной БД
Зачастую требуется хранить какие-то иерархичные данные, например, подобие файловой системы или какую-то организационную структуру.
1. id + parent_id
Самый простой подход, при котором храним идентификатор родительской сущности (либо null, если сущность и так корневая).
Плюсы:
- Простота модели
- Простая вставка
- Простой перенос поддерева
Минусы:
- “Дерево” может стать не деревом - МД не запрещает циклические ссылки
- Рекурсивные запросы, чтобы доставать поддерево по id корня
2. id, path + parent_id, parent_path
Помимо id добавляется path - путь по айдишникам до текущей сущности. Это нам позволяет сделать более интересные констрейнты и гарантировать, что мы имеем дело реально с деревом.
Здесь мы гарантируем, что
1) path корневой папки - это /<id>/
2) path некорневой папки - это <parent_path><id>/
Что позволяет обеспечить отсутствие циклов
Плюсы:
- Гарантия отсутствия циклов
- Простой запрос поддерева - where path like ‘/1/2/3/%’
Минусы:
- Сложность модели
- Более сложная вставка
- Сложный перенос поддерева
Зачастую требуется хранить какие-то иерархичные данные, например, подобие файловой системы или какую-то организационную структуру.
1. id + parent_id
create table folders
(
id bigserial not null,
parent_id bigint null references folders (id),
data jsonb not null
);
Самый простой подход, при котором храним идентификатор родительской сущности (либо null, если сущность и так корневая).
Плюсы:
- Простота модели
- Простая вставка
- Простой перенос поддерева
Минусы:
- “Дерево” может стать не деревом - МД не запрещает циклические ссылки
- Рекурсивные запросы, чтобы доставать поддерево по id корня
2. id, path + parent_id, parent_path
Помимо id добавляется path - путь по айдишникам до текущей сущности. Это нам позволяет сделать более интересные констрейнты и гарантировать, что мы имеем дело реально с деревом.
create table folders
(
id bigserial not null,
path varchar not null,
parent_id bigint null,
parent_path varchar null,
data jsonb not null
constraint ck__folders__path
check (path = coalesce(parent_path, '/') || id || '/'),
-- нужен для FK
constraint uc__folders__path__id
unique (path, id),
constraint fk__folders__parent_id__parent_path
foreign key (parent_path, parent_id) references folders (path, id)
match full
);
Здесь мы гарантируем, что
1) path корневой папки - это /<id>/
2) path некорневой папки - это <parent_path><id>/
Что позволяет обеспечить отсутствие циклов
Плюсы:
- Гарантия отсутствия циклов
- Простой запрос поддерева - where path like ‘/1/2/3/%’
Минусы:
- Сложность модели
- Более сложная вставка
- Сложный перенос поддерева