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 эти две формы эквивалентны.
