Урок по треку SQL и базы данных 6 мин

JOIN за 6 минут

INNER, LEFT, FULL, CROSS, self join и ловушки с условиями в ON и WHERE.

JOIN склеивает строки двух таблиц по условию. Представьте: для каждой пары строк (левая, правая) проверяется условие из ON. Пары, где оно true, попадают в результат.

SELECT o.id, c.name
FROM orders o
JOIN customers c ON c.id = o.customer_id;

Виды JOIN

  • INNER JOIN (или просто JOIN): только пары, где условие выполнено. Заказ без клиента и клиент без заказов пропадут.
  • LEFT JOIN: все строки левой таблицы. Если пары справа нет, правые колонки заполняются NULL.
  • RIGHT JOIN: зеркально, все строки правой. На практике переписывают в LEFT, поменяв таблицы местами.
  • FULL JOIN: все строки обеих таблиц, недостающая сторона NULL.
  • CROSS JOIN: декартово произведение, каждая строка с каждой. 100 × 100 = 10 000 строк.

Размножение строк

JOIN не «подтягивает одно значение»: если у клиента 3 заказа, клиент появится в результате 3 раза. Если затем приджойнить ещё таблицу «один ко многим» (например, отзывы), строки перемножатся: 3 заказа × 2 отзыва = 6 строк. Отсюда классический баг: SUM(order.total) после двух JOIN считает каждый заказ несколько раз.

Лечится агрегацией до JOIN (в подзапросе или CTE) или отдельными подзапросами.

ON против WHERE в LEFT JOIN

Для INNER JOIN разницы почти нет. Для LEFT JOIN огромная:

-- Все клиенты и их оплаченные заказы (у кого нет, будет NULL)
SELECT c.name, o.id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id AND o.status = 'paid';

-- Ошибка: WHERE выкинет строки с NULL, LEFT превратился в INNER
SELECT c.name, o.id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'paid';

Правило: условие на правую таблицу LEFT JOIN, которое должно фильтровать «что приклеить», пишите в ON.

Anti-join: «у кого нет»

Найти клиентов без заказов:

SELECT c.*
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;

Равноценная и часто более читаемая форма: WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id).

Self join

Таблица соединяется сама с собой через разные псевдонимы. Классика: сотрудник и его руководитель.

SELECT e.name, m.name AS manager
FROM employees e
LEFT JOIN employees m ON m.id = e.manager_id;

USING и NATURAL

JOIN orders USING (customer_id) работает, когда колонки называются одинаково. NATURAL JOIN соединяет по всем одноимённым колонкам: опасно, добавили колонку created_at в обе таблицы, и запрос молча сломался.

Частые ошибки

  • Фильтр по правой таблице LEFT JOIN в WHERE.
  • Неучтённое размножение строк перед SUM или COUNT.
  • Забытое условие соединения, получился CROSS JOIN.
  • NULL в ключе соединения: NULL = NULL не true, такие строки не склеятся.

Что спросят на собесе

  • Чем отличаются INNER, LEFT, FULL и CROSS JOIN, сколько строк будет в результате.
  • Как найти записи без пары в другой таблице.
  • Почему условие в WHERE ломает LEFT JOIN.
  • Почему после JOIN сумма получилась больше, чем должна.
  • Как вывести сотрудника вместе с именем руководителя.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.