SQLJOINLEFT JOINtutorial

Что такое LEFT JOIN в SQL? Разбор для начинающих

LEFT JOIN — соединение таблиц, при котором ВСЕ строки левой таблицы попадают в результат. Если справа пары нет, ставится NULL. Разбираем синтаксис, отличие от INNER JOIN, поиск 'сирот' и три задачи.

6 мин чтенияСправочникSQL · JOIN · LEFT JOIN · tutorial · beginner

LEFT JOIN (или полное название LEFT OUTER JOIN) — это соединение, при котором все строки левой таблицы попадают в результат. Если в правой таблице нет совпадения, на её колонках будет NULL.

Контрастно с INNER JOIN: тот выкидывает строки без пары. LEFT JOIN их сохраняет, просто помечает «справа ничего».

Аналогия. Учитель раздаёт ученикам тесты на проверку. Не все сдали.

  • INNER JOIN — «покажи мне только тех, кто сдал тест и оценку».
  • LEFT JOIN — «покажи мне всех учеников, и если сдали — оценку, если нет — пустое место».

Второй вариант чаще нужен в реальной жизни — нужно ВИДЕТЬ, кто отстал.

Зачем нужен LEFT JOIN

Классические сценарии:

  1. Список с дополнительной информацией, даже если её нет. Например, все пользователи + сколько у них заказов (включая 0).
  2. Найти «сирот» — записи без пары. Пользователи, у которых нет ни одного заказа. Товары, которых ни разу не покупали. И так далее.
  3. Подсчёт «дыр» в данных. Сколько постов без комментариев, сколько уроков без домашек.

INNER JOIN для этих задач не подходит — он сразу выбрасывает «бездетные» строки.

Базовый синтаксис

SELECT колонки
FROM таблица_A         -- левая
LEFT JOIN таблица_B    -- правая
  ON A.ключ = B.ключ;

«Левая» — это та, что после FROM. «Правая» — после LEFT JOIN. Запомни направление: LEFT — значит, левая полностью сохраняется.

OUTER можно опускать. LEFT JOIN и LEFT OUTER JOIN — это одно и то же.

Пример: школа

students:

id name
1 Анна
2 Борис
3 Вера
4 Григорий

grades (оценки за тест):

id student_id score
10 1 5
11 1 4
12 2 3

Запрос: «все ученики и их оценки, если есть».

SELECT s.name, g.score
FROM students s
LEFT JOIN grades g ON s.id = g.student_id;

Результат:

name score
Анна 5
Анна 4
Борис 3
Вера NULL
Григорий NULL

Что видно:

  • Все 4 ученика в результате. Это и есть смысл LEFT JOIN.
  • У Анны две оценки — две строки.
  • У Бориса одна — одна строка.
  • У Веры и Григория нет оценок → одна строка с NULL в score.

Если бы мы написали INNER JOIN, в результате остались бы только Анна (2 строки) и Борис. Вера и Григорий выпали бы — а ведь именно они интересны учителю.

Поиск «сирот» — записей без пары

Классический трюк. Чтобы найти учеников, которые не написали ни одного теста, фильтруй на IS NULL:

SELECT s.name
FROM students s
LEFT JOIN grades g ON s.id = g.student_id
WHERE g.id IS NULL;

Результат:

name
Вера
Григорий

Логика: LEFT JOIN сохранил всех учеников. У тех, у кого нет оценок, в g.id стоит NULL. Фильтр WHERE g.id IS NULL оставляет именно их.

Этот паттерн (LEFT JOIN ... WHERE other.id IS NULL) — самый частый способ найти «нет совпадения». Используется везде: пользователи без заказов, посты без комментариев, товары, которые никто не купил.

Не используй для фильтра колонку правой таблицы

Ловушка. Если хочешь оставить только тех, кто получил 5, и пишешь:

SELECT s.name, g.score
FROM students s
LEFT JOIN grades g ON s.id = g.student_id
WHERE g.score = 5;

Результат:

name score
Анна 5

Вера и Григорий выпали. Потому что условие WHERE g.score = 5 для них даёт NULL = 5 (это не TRUE), и они отфильтровались. По сути LEFT JOIN превратился в INNER JOIN.

Если хочешь оставить всех учеников, но только пятёрки в правой части — условие надо ставить в ON, а не в WHERE:

SELECT s.name, g.score
FROM students s
LEFT JOIN grades g ON s.id = g.student_id AND g.score = 5;

Результат:

name score
Анна 5
Борис NULL
Вера NULL
Григорий NULL

Теперь все четверо в результате; оценка показана только если она 5. Различие тонкое, но критичное. Запомни: условия по правой таблице — в ON, по левой — в WHERE.

INNER JOIN vs LEFT JOIN — наглядная разница

Представь две команды футболистов: players (игроки) и goals (забитые голы за матч). Не каждый игрок забил.

  • INNER JOIN → только игроки, которые забили хотя бы один гол.
  • LEFT JOIN от players к goals → все игроки, у тех, кто не забил, в колонке гола стоит NULL.
  • LEFT JOIN + WHERE goals.id IS NULL → только игроки, которые не забили ни одного гола.

Три разных вопроса — три разных запроса.

Большой пример: e-commerce

users:

id name signup_date
1 Анна 2024-01-15
2 Борис 2024-02-01
3 Вера 2024-02-20
4 Григорий 2024-03-10

orders:

id user_id amount created_at
50 1 500 2024-02-05
51 1 1500 2024-02-10
52 2 200 2024-02-25

Вопрос бизнеса: «сколько заказов и общая сумма у каждого зарегистрированного пользователя? Включая тех, кто пока ничего не купил».

SELECT
  u.name,
  COUNT(o.id)               AS orders_count,
  COALESCE(SUM(o.amount), 0) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name
ORDER BY total_spent DESC;

Результат:

name orders_count total_spent
Анна 2 2000
Борис 1 200
Вера 0 0
Григорий 0 0

Важные нюансы:

  • Все 4 пользователя в результате, даже Вера и Григорий, у которых нет заказов.
  • COUNT(o.id) для них даёт 0 (NULL не считается в COUNT).
  • SUM для них вернул бы NULL, поэтому обернули в COALESCE(..., 0) — заменяет NULL на 0 для красивого вывода.

Это типовой запрос для дашбордов: «активность каждого пользователя, включая нулевую».

Частые ошибки новичков

1. WHERE на правой таблице, который превращает LEFT в INNER. Самый частый и самый коварный косяк. Если фильтр опирается на колонку правой части — клади его в ON. Если на левую — в WHERE.

2. Не понимать NULL в результате. Видят NULL и пугаются. На самом деле это нормальный сигнал «справа нет пары». Используй IS NULL, COALESCE, агрегаты, которые умеют игнорировать NULL — и всё ок.

3. Использовать LEFT JOIN, когда нужен INNER. Если бизнес-задача «только пользователи, у которых ЕСТЬ заказы» — INNER JOIN. LEFT JOIN тут даст лишние строки и потенциально замедлит запрос.

4. Несколько LEFT JOIN-ов и логика теряется. Когда соединяешь A LEFT JOIN B LEFT JOIN C, помни: каждое следующее условие — относительно того, что получилось до. На сложных JOIN-цепочках лучше пошагово отлаживать.

5. RIGHT JOIN вместо переворота. RIGHT JOIN существует, делает то же самое, но с противоположной стороны. На практике почти не используется — проще поменять таблицы местами и оставить LEFT JOIN. Так читать понятнее.

6. COUNT(*) вместо COUNT(колонка) после LEFT JOIN. COUNT(*) считает все строки, включая те, где правая часть NULL. COUNT(o.id) считает только реальные совпадения. На LEFT JOIN это даёт разный результат.

Мини-резюме

  • LEFT JOIN сохраняет ВСЕ строки левой таблицы.
  • Если в правой нет пары — на её колонках стоит NULL.
  • LEFT JOIN ... WHERE other.id IS NULL — стандартный способ найти строки без пары.
  • Условия по правой таблице клади в ON, по левой — в WHERE.
  • OUTER опционален; LEFT JOIN = LEFT OUTER JOIN.
  • Чаще нужен в реальных задачах, чем INNER, — потому что обычно мы хотим видеть полную картину, включая «нули».

Закрепи на практике

Решай задачи в SQL-тренажёре с мгновенной проверкой и подсказками.

Открыть тренажёр