Соединение трёх и более таблиц
Содержание курса
Разберём, как соединить три таблицы в SQL и проследить, откуда берётся каждая строка результата.
Синтаксис двух JOIN подряд и логика цепочки соединений
Представьте, что нужно получить даты заказов, названия купленных товаров и количество единиц. Эти данные лежат в трёх таблицах: orders, order_items и products. Прямой связи между заказом и товаром нет: её хранит промежуточная таблица order_items. Такая цепочка описывается двумя JOIN подряд:
SELECT o.order_date, p.title, oi.quantity
FROM orders AS o
JOIN order_items AS oi ON o.order_id = oi.order_id
JOIN products AS p ON oi.product_id = p.product_id;
Первый JOIN прикрепляет позиции заказа к orders по условию o.order_id = oi.order_id. На этом шаге каждая строка результата описывает одну позицию конкретного заказа. Второй JOIN добавляет сведения о товаре по условию oi.product_id = p.product_id. Он опирается на oi.product_id — столбец таблицы, которая уже находится в цепочке.
Здесь ключевое правило: каждое условие ON связывает новую таблицу с теми, что уже упомянуты выше. Написать ON p.product_id = o.order_id синтаксически допустимо, но это сопоставит разные сущности и даст случайные пары либо пустой результат.
Логически каждый JOIN добавляет к цепочке одну таблицу. Физически оптимизатор может выбрать другой порядок выполнения для скорости, но на смысл результата это не влияет: запрос описывает, какие ключи связываются.
Если к этой цепочке нужно добавить клиента, перед order_items появляется ещё один JOIN: customers → orders → order_items → products. Число JOIN само по себе не говорит о плохой схеме. Проверять нужно другое: понятна ли цепочка ключей, верна ли кратность связей, не дублируется ли логика и что показывает план выполнения.
Как меняются строки после каждого JOIN
У заказа №1 две позиции: товар №10 в количестве 2 и товар №20 в количестве 1. В справочнике товаров №10 — клавиатура, №20 — мышь.
| Этап | Строки результата |
|---|---|
| orders | 1 · 2026-11-01 |
| orders JOIN order_items | 1 · 2026-11-01 · 10 · 2 1 · 2026-11-01 · 20 · 1 |
| Ещё JOIN products | 2026-11-01 · Клавиатура · 2 2026-11-01 · Мышь · 1 |
После первого соединения заказ занимает две строки, потому что в нём две позиции. Второе соединение добавляет названия товаров. Здесь ключ products.product_id уникален, поэтому строк больше не становится.
Этот пример можно запустить отдельно в SQLite — данные уже заданы через CTE:
WITH orders(order_id, order_date) AS (VALUES (1, '2026-11-01')),
order_items(order_id, product_id, quantity) AS (VALUES (1, 10, 2), (1, 20, 1)),
products(product_id, title) AS (VALUES (10, 'Клавиатура'), (20, 'Мышь'))
SELECT o.order_date, p.title, oi.quantity
FROM orders AS o
JOIN order_items AS oi ON o.order_id = oi.order_id
JOIN products AS p ON oi.product_id = p.product_id
ORDER BY p.product_id;Результат — две строки из последней строки таблицы выше. Если справочник содержит несколько записей с одним product_id, JOIN размножит соответствующую позицию заказа; дальше в уроке разберём, как это влияет на суммы.
