Собесов

Связи в реляционной модели: 1:1, 1:M, M:M

SQLПроектирование БДЛёгкаяJunior

Условие

Какой тип связи устанавливается, если каждый объект из одной таблицы может быть связан с несколькими объектами из другой, и наоборот? Как реализуется в БД? Чем отличается «один-ко-многим»?

Решение

Три типа связей

Связь Пример Реализация
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 — отдельная колонка.

Подводные камни

  1. «Просто добавлю массив client_ids» — нарушает 1NF; и FK невозможен. Делайте промежуточную таблицу.
  2. UNIQUE для 1:1 — забывают, без него связь становится 1:M.
  3. Удаление в M:MON DELETE CASCADE от обеих родительских таблиц должен быть согласован.
  4. Cardinality на ER — Crow's foot нотация: || (один), o< (ноль или много).

Эталонный ответ

1:M — каждой записи в первой таблице соответствует много записей во второй; FK живёт на «многих» (например, orders.client_id). M:M — реализуется через промежуточную таблицу (associative table) с составным PK.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти