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

LEFT JOIN и NULL в результате соединения

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

Почему LEFT JOIN возвращает NULL

LEFT JOIN сохраняет строки слева, даже если справа не нашлось пары. Этот запрос можно выполнить в SQLite без подготовки таблиц:

WITH customers(id, name) AS (VALUES (1, 'Anna'), (2, 'Boris')),
orders(id, customer_id) AS (VALUES (101, 1), (102, 1))
SELECT c.name, o.id AS order_id
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id
ORDER BY c.id, o.id;

Результат: Anna | 101, Anna | 102, Boris | NULL. Два заказа дают две строки для Анны; отсутствие заказа не удаляет Бориса. INNER JOIN вернул бы только первые две строки. Ниже разбираем поиск клиентов без заказов и влияние фильтра WHERE.

В прошлом уроке INNER JOIN оставлял в результате только те строки, для которых нашлось совпадение в обеих таблицах. Клиент без заказов просто исчезал. Это нормально, когда нужны только «парные» данные, — но часто задача ровно обратная: показать всех клиентов и отметить, у кого заказов нет. Здесь в игру входит LEFT JOIN.

LEFT JOIN: синтаксис, гарантия сохранения строк левой таблицы и NULL в правой части

Синтаксис LEFT JOIN выглядит почти так же, как INNER JOIN — меняется только ключевое слово:

SELECT c.name, o.order_id, o.total_amount
FROM customers AS c
LEFT JOIN orders AS o ON c.customer_id = o.customer_id;

Предположим, в базе три клиента и два заказа — оба принадлежат Анне:

customer_id name
1 Анна
2 Борис
3 Вера
order_id customer_id order_date total_amount
101 1 2024-01-10 5000
102 1 2024-02-15 3000

Запрос вернёт четыре строки:

name order_id total_amount
Анна 101 5000
Анна 102 3000
Борис NULL NULL
Вера NULL NULL

Механизм такой: для каждой строки левой таблицы (customers) SQLite ищет строки в правой (orders), где выполняется условие ON. Если совпадение найдено — данные объединяются обычным образом. Если не найдено — строка из customers всё равно попадает в результат, а все столбцы из orders принимают значение NULL.

Для Анны совпадений два, поэтому она появляется в результате дважды. Для Бориса и Веры совпадений ноль — они появляются по одному разу, но order_id и total_amount у них NULL.

NULL здесь не означает, что в таблице orders испорчены данные. Это сигнал: связанной записи не существует. Именно этим NULL в правых столбцах LEFT JOIN отличается от NULL, который мог бы стоять в самой таблице orders как реально пропущенное значение поля.

Добавление слова OUTER (LEFT OUTER JOIN) синтаксически допустимо, но не меняет поведение — в SQLite эти две формы эквивалентны.

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

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

О переписке

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

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

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