Соединение трёх и более таблиц
Содержание курса
WHERE и ORDER BY применяются к результату всех JOIN
WHERE и ORDER BY в многотабличном запросе — не фильтр отдельной таблицы, а операция над уже собранным результатом всех соединений. Логически сначала формируется результат соединений, затем WHERE отсекает лишние строки, а ORDER BY сортирует остаток. Физический план может применить фильтр раньше или переставить JOIN ради скорости, сохраняя тот же результат.
Позиция клозов жёстко фиксирована синтаксисом: WHERE идёт после последнего JOIN, ORDER BY — в самом конце:
SELECT c.name AS клиент,
o.order_date AS дата,
p.title AS товар
FROM customers AS c
JOIN orders AS o ON c.customer_id = o.customer_id
JOIN order_items AS oi ON o.order_id = oi.order_id
JOIN products AS p ON oi.product_id = p.product_id
WHERE p.price > 10000
ORDER BY c.name;
Здесь p.price > 10000 фильтрует строки объединённого набора — те сочетания клиент–заказ–товар, где цена товара не превышает порог, просто не попадут в итог. ORDER BY c.name сортирует оставшееся по имени клиента.
В условии WHERE и в ORDER BY используются те же псевдонимы, что объявлены в FROM/JOIN. Сейчас WHERE price > 10000 тоже сработает: в показанной схеме имя price однозначно. Запись p.price предпочтительнее для ясности и защитит запрос, если позже в цепочке появится другой одноимённый столбец.
Почему сумма заказа удваивается после JOIN
Проверьте кратность до расчёта итогов. Если один заказ на 100 рублей содержит две позиции, JOIN с order_items создаст две строки: сумма заказа 100 повторится в каждой. Поэтому SUM(o.total_amount) после соединения даст 200, хотя заказ был один. Это не ошибка SQL: соединение сохраняет по строке на каждую позицию.
WITH orders(order_id, total_amount) AS (VALUES (1, 100)),
order_items(order_id, quantity) AS (VALUES (1, 1), (1, 2))
SELECT COUNT(*) AS rows_after_join,
SUM(o.total_amount) AS inflated_total
FROM orders AS o
JOIN order_items AS oi ON oi.order_id = o.order_id;Запрос вернёт 2 | 200. Для выручки по заказам агрегируйте таблицу orders без присоединения позиций; если нужна сумма товаров, считайте её на уровне строк order_items с соответствующей ценой. SUM(DISTINCT o.total_amount) не исправляет проблему: два разных заказа с одинаковой суммой схлопнутся. Перед добавлением третьего JOIN полезно сравнить количество строк до и после каждого соединения. Отдельная задача на условие JOIN помогает заметить случаи, когда число строк выросло из-за неверного ON.
ORDER BY может сортировать по любому столбцу из результата, в том числе по столбцу, которого нет в SELECT. Это работает, потому что сортировка применяется к строкам объединённого набора, а не только к тому, что выводится.
