Соединение таблиц

Соединение трёх и более таблиц

Содержание курса

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. Это работает, потому что сортировка применяется к строкам объединённого набора, а не только к тому, что выводится.

Начать заново?

Помощник начнёт новый диалог по этой задаче. Текущая переписка останется в истории до конца срока хранения.

О переписке

Можно приложить до трёх фото PNG, JPEG или WebP по 5 МБ.

Переписка и фото хранятся 90 дней после последнего принятого сообщения. Преподаватель получит доступ к диалогу, когда вы попросите его помочь.

Проверка решения остаётся отдельным действием.