Условие
Какой тип связи устанавливается, если каждый объект из одной таблицы может быть связан с несколькими объектами из другой, и наоборот? Как реализуется в БД? Чем отличается «один-ко-многим»?
Решение
Три типа связей
| Связь | Пример | Реализация |
|---|---|---|
| 1:1 | Паспорт ↔ человек | FK с UNIQUE на одной из сторон |
| 1:M (один-ко-многим) | Клиент ↔ заказы | FK на «многих» ссылается на «одного» |
| M:M (многие-ко-многим) | Студент ↔ курсы | Промежуточная таблица |
1:M (один-ко-многим)
Самый частый тип. Каждой записи в одной таблице соответствует много записей в другой, но каждой записи во второй — только одна в первой.
CREATE TABLE clients (
id BIGSERIAL PRIMARY KEY,
name TEXT
);
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
client_id BIGINT REFERENCES clients(id), -- FK на «одного»
total NUMERIC
);Один клиент — много заказов. Один заказ принадлежит одному клиенту. FK живёт на стороне «многих».
M:M (многие-ко-многим)
Прямо реализовать нельзя — нужна промежуточная (associative) таблица:
CREATE TABLE students (id BIGINT PRIMARY KEY, name TEXT);
CREATE TABLE courses (id BIGINT PRIMARY KEY, title TEXT);
CREATE TABLE enrollments (
student_id BIGINT REFERENCES students(id),
course_id BIGINT REFERENCES courses(id),
enrolled_at DATE,
PRIMARY KEY (student_id, course_id) -- составной PK
);Каждая строка enrollments — один факт «студент записан на курс». Можно добавить атрибуты связи: дату записи, оценку, статус.
1:1
Реже, чем кажется. Обычно отделяют редкие или большие атрибуты:
CREATE TABLE users (id BIGSERIAL PRIMARY KEY, email TEXT);
CREATE TABLE user_profiles (
user_id BIGINT PRIMARY KEY REFERENCES users(id), -- PK = FK = UNIQUE
bio TEXT,
avatar BYTEA
);Когда оставлять 1:1 в одной таблице, а когда разделять — решение по производительности и приватности.
Идентифицирующая vs неидентифицирующая связь
- Идентифицирующая: дочерняя таблица не существует без родителя, FK входит в PK (
enrollmentsвыше). - Неидентифицирующая: дочерняя сущность имеет свой PK, FK — отдельная колонка.
Подводные камни
- «Просто добавлю массив client_ids» — нарушает 1NF; и FK невозможен. Делайте промежуточную таблицу.
UNIQUEдля 1:1 — забывают, без него связь становится 1:M.- Удаление в M:M —
ON DELETE CASCADEот обеих родительских таблиц должен быть согласован. - Cardinality на ER — Crow's foot нотация:
||(один),o<(ноль или много).
Эталонный ответ
1:M — каждой записи в первой таблице соответствует много записей во второй; FK живёт на «многих» (например, orders.client_id). M:M — реализуется через промежуточную таблицу (associative table) с составным PK.