Моделирование связей: один-ко-многим

Учимся разносить данные по связанным таблицам вместо одной большой.

Связь один-ко-многим — это когда одной строке в первой таблице соответствует много строк во второй (один клиент — много заказов).

Почему не одна большая таблица

Заманчиво хранить всё в одной таблице: имя клиента, его город и тут же каждый его заказ. Но тогда имя и город клиента повторяются в каждой строке заказа. Это плохо: данные раздуваются, а при смене города придётся править десятки строк и легко ошибиться. Решение — разнести данные по таблицам и связать их ключами.

Две таблицы и связь

Сделаем таблицу клиентов и таблицу заказов. У каждого заказа есть customer_id — ссылка на клиента. Один клиент может иметь много заказов: это и есть связь один-ко-многим, а внешний ключ ставится на стороне «многих» (в заказах).

CREATE TABLE customers (
    id   INTEGER PRIMARY KEY,
    name TEXT
);

CREATE TABLE orders (
    id          INTEGER PRIMARY KEY,
    customer_id INTEGER,   -- ссылка на customers.id
    amount      INTEGER
);

INSERT INTO customers (id, name) VALUES (1, 'Аня'), (2, 'Борис'), (3, 'Вера');
INSERT INTO orders (customer_id, amount) VALUES
    (1, 500), (1, 1200), (2, 800);

SELECT * FROM orders;

Вывод:

1|1|500
2|1|1200
3|2|800

Видно, что у Ани (id = 1) два заказа, у Бориса (id = 2) один, а у Веры (id = 3) пока ни одного. Имя клиента нигде не дублируется — оно хранится один раз в таблице customers.

Как связать данные обратно

Сейчас в заказах виден только числовой customer_id, а не имя. Чтобы показать имя клиента рядом с заказом, таблицы нужно «соединить» по этому ключу. Это делает оператор JOIN — ему посвящён следующий урок. Связь, которую мы спроектировали, — фундамент: дальше всё построено на ней.

Другие виды связей (коротко)

  • Один-ко-многим — самый частый: клиент → заказы, автор → книги, категория → товары. Ключ на стороне «многих».
  • Один-к-одному — редкая: например, пользователь и его расширенный профиль в отдельной таблице.
  • Многие-ко-многим — например, студенты и курсы: один студент на многих курсах, на курсе много студентов. Реализуется через третью, связующую таблицу с двумя внешними ключами.

Итог

  • Данные разносят по таблицам, чтобы не дублировать и не рассогласовывать их.
  • В связи один-ко-многим внешний ключ ставится на стороне «многих» (в заказах — customer_id).
  • Связь многие-ко-многим реализуют через отдельную связующую таблицу.
Проверьте себя
1. В связи один-ко-многим (клиент — заказы) где ставится внешний ключ?
AВ таблице клиентов
BВ таблице заказов (на стороне «многих»)
CВ обеих таблицах
DВнешний ключ не нужен
2. Зачем разносить данные по нескольким таблицам?
AЧтобы запросы были длиннее
BЧтобы не дублировать данные и не рассогласовывать их при изменениях
CЭто требование SQLite
DЧтобы занять больше места
3. Как обычно реализуют связь многие-ко-многим?
AДвумя внешними ключами в одной из таблиц
BЧерез отдельную связующую таблицу с двумя внешними ключами
CСвязь многие-ко-многим невозможна
DЧерез DISTINCT
Поддержать проект