Представь, тебе нужно описать граф в реляционной модели
Скорее всего получится приблизительно такое:
create table graph (
id bigserial primary key
);
create table node (
id bigserial primary key,
graph_id bigint
);
create table edge (
id bigserial primary key,
from_id bigint,
to_id bigint
);
Далее добавим внешние ключи, чтобы гарантировать целостность
node (graph_id) -> graph (id)
edge (from_id) -> node (id)
edge (to_id) -> node (id)
И на этом в целом можно закончить
Однако схема позволяет создать ребро, которое будет связывать две вершины из разных графов:
graph(id = 1)
graph(id = 2)
node(id = 1, graph_id = 1)
node(id = 2, graph_id = 2)
edge(id = 1, from_id = 1, to_id = 2) // no error
Валидировать такое можно с помощью
- триггеров
- проверок на уровне приложения
- многоколоночных внешних ключей
С первыми двумя вариантами в целом все ясно, а третий рассмотрим подробнее. Реализовать его можно так:
1. Добавляем в таблицу edge колонку graph_id
2. Для ребер многоколоночные внешние ключи:
node (graph_id) -> graph (id)
edge (graph_id, from_id) -> node (graph_id, id)
edge (graph_id, to_id) -> node (graph_id, id)
Такие ключи уже на уровне схемы данных не позволят привязать ребро к вершинам из разных графов
p.s.: для того чтобы сделать многоколоночный внешний ключ, на целевой таблице этот набор колонок должен быть уникальным — в нашем случае должен быть primary key / unique index на node (graph_id, id)