SQL LEFT JOIN: строки даже без совпадений

SQL LEFT JOIN возвращает каждую строку из левой таблицы и добавляет совпавшие строки из правой таблицы. В терминах SQL это внешнее соединение: когда совпадения нет, столбцы правой стороны заполняются значениями NULL. Используйте его, когда нужны все записи из одной таблицы независимо от наличия связанных данных, например список всех клиентов, включая тех, кто не оформлял заказ. LEFT JOIN и LEFT OUTER JOIN — одинаковые ключевые слова; OUTER можно опустить.

Пример SQL LEFT JOIN Для Возврата Всех Строк

Вывод:

Результат появится здесь...

Вывод:

+--------+-------+
| name   | total |
+--------+-------+
| Алина  | 79    |
| Алина  | 150   |
| Борис  | NULL  |
| Карина | 45    |
+--------+-------+
4 row(s)

Как Работает Этот Пример

  1. customers хранит трех клиентов; orders хранит три заказа, связанные по customer_id.
  2. LEFT JOIN orders o ON o.customer_id = c.id сопоставляет каждого клиента с каждым заказом, где customer_id равен его id. У Алины два заказа, поэтому в выводе две строки.
  3. У Бориса заказов нет. LEFT JOIN сохраняет его строку и подставляет NULL в o.total, а не удаляет строку из результата (так сделал бы INNER JOIN).
  4. ORDER BY c.id, o.id держит вывод сгруппированным по клиентам и стабильным внутри каждой группы. NULL в столбце total у Бориса означает отсутствие совпадения, а не заказ с нулевой суммой.

ON vs WHERE: Почему Важно Место Фильтра

Размещение фильтра правой таблицы в WHERE вместо ON — самая частая ошибка с LEFT JOIN: она незаметно превращает внешнее соединение во внутреннее.

-- Неправильно: WHERE удаляет Бориса, потому что у него o.total = NULL
SELECT c.name, o.total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.total > 50;

-- Правильно: ON фильтрует во время сопоставления, Борис остается с NULL
SELECT c.name, o.total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id AND o.total > 50;

Условия в ON вычисляются во время соединения: несовпавшие строки слева все равно остаются и получают NULL. WHERE применяется после соединения, поэтому отфильтровывает любые строки, где правый столбец равен NULL, то есть удаляет именно те строки, которые LEFT JOIN должен сохранять.

Anti-Join: Поиск Строк Без Совпадений

LEFT JOIN в сочетании с проверкой IS NULL по столбцу правой таблицы — стандартный способ найти отсутствующие связанные записи:

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

Запрос вернет только Бориса — клиента без заказов. Паттерн работает, потому что o.id равен NULL только у несовпавших строк слева. NOT EXISTS дает тот же результат и может быть нагляднее, когда столбцы правой таблицы в выводе не нужны.

LEFT JOIN vs INNER JOIN

LEFT JOININNER JOIN
Все строки слева, NULL для несовпавших столбцов справаТолько строки с совпадением на обеих сторонах
Число строк ≥ числу строк левой таблицыЧисло строк ≤ числу строк меньшей таблицы
Используйте, когда важны строки без совпадений (отчеты, проверки полноты)Используйте, когда каждая строка результата должна иметь данные из обеих таблиц

Правило: начинайте с LEFT JOIN, когда важно сохранить все строки левой таблицы. Переключайтесь на INNER JOIN, когда строки без совпадений не нужны или считаются некорректными.

Частые Ошибки С SQL LEFT JOIN

Умножение строк в соединениях один-ко-многим

LEFT JOIN возвращает одну строку результата на каждую совпавшую пару. Если у одного клиента десять заказов, этот клиент появится десять раз. Агрегации без учета этого эффекта завышают итоги.

-- Неправильно: считает строки заказов, а не клиентов
SELECT COUNT(*) FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;

-- Правильно: считает уникальных клиентов
SELECT COUNT(DISTINCT c.id) FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;

Использование SELECT * в соединениях

SELECT * в запросах с JOIN возвращает дублирующиеся имена столбцов (и c.id, и o.id), что создает путаницу в коде приложения. Явно перечисляйте только нужные столбцы.

Заметки О Производительности

Индексируйте ключ соединения в правой таблице (orders.customer_id), чтобы движок находил совпадения по индексу, а не полным сканированием. Для больших правых таблиц, где нужна только часть строк, предварительно фильтруйте данные подзапросом или переносите условие в ON, а не в WHERE, чтобы уменьшить объем работы и сохранить семантику LEFT JOIN.

FAQ

В чем разница между LEFT JOIN и LEFT OUTER JOIN?

Разницы нет. LEFT JOIN и LEFT OUTER JOIN — синонимы, определенные стандартом SQL. Во всех популярных СУБД (PostgreSQL, MySQL, SQLite, SQL Server, Oracle) они работают одинаково. Обычно OUTER опускают для краткости.

Почему LEFT JOIN возвращает меньше строк после добавления WHERE?

Фильтр WHERE по столбцу правой таблицы удаляет строки, расширенные NULL, и фактически превращает LEFT JOIN во внутреннее соединение. Перенесите условие в ON, чтобы сохранить несовпавшие строки левой таблицы в результате.

Когда использовать LEFT JOIN вместо INNER JOIN?

Используйте LEFT JOIN, когда строки левой стороны должны попадать в результат независимо от совпадений: отчеты полноты, поиск пропущенных данных и необязательное обогащение. Используйте INNER JOIN, когда валидны только совпавшие строки, а несовпавшие нужно исключить.