Як працює left join?
LEFT JOIN повертає всі рядки з лівої таблиці плюс відповідні рядки з правої. Де збігу немає, стовпці правої таблиці заповнюються NULL.
Теорія
TL;DR
- Уяви, що ліва таблиця це список запрошених на вечірку. Кожен зі списку потрапить у результат. Права таблиця це замовлення їжі. Немає замовлення = порожня тарілка (NULL).
- Кожен рядок лівої таблиці з'явиться в результаті мінімум один раз. Незбіглі праві рядки просто відкидаються.
- Використовуй LEFT JOIN коли потрібні всі записи зліва, навіть якщо праворуч немає збігу.
Швидкий приклад
-- таблиця clients (ліва)
-- client_id | name
-- 1 | Alice
-- 2 | Bob
-- 3 | Carol
-- таблиця purchases (права)
-- purchase_id | client_id | item
-- 1 | 1 | Laptop
-- 2 | 1 | Mouse
-- У Bob і Carol немає покупок
SELECT c.name, p.item
FROM clients c
LEFT JOIN purchases p ON c.client_id = p.client_id;
-- Результат:
-- Alice | Laptop
-- Alice | Mouse
-- Bob | NULL
-- Carol | NULLВсі троє клієнтів є в результаті. Bob і Carol отримали NULL для item, бо в purchases немає відповідних рядків.
Головна відмінність від INNER JOIN
INNER JOIN повертає тільки ті рядки, де є збіг в обох таблицях. Bob і Carol просто зникнуть з результату. LEFT JOIN зберігає їх і заповнює праві стовпці NULL. Ось і вся суть.
Коли використовувати
- Всі клієнти та їхні замовлення, включно з тими хто нічого не замовив: LEFT JOIN.
- Тільки клієнти з хоча б одним замовленням: INNER JOIN.
- Повне злиття де обидві сторони можуть мати прогалини: FULL OUTER JOIN.
- Пріоритет правої таблиці: RIGHT JOIN, але більшість команд просто міняє таблиці місцями і пише LEFT JOIN.
Як це працює всередині
Рушій перебирає кожен рядок лівої таблиці. Для кожного шукає відповідні рядки в правій за умовою ON. Якщо збіг є, виводить об'єднаний рядок. Якщо ні, виводить лівий рядок із NULL у всіх правих стовпцях.
Індекс на ключі з'єднання (наприклад, client_id в purchases) прискорює це суттєво. Без індексу на великій таблиці рушій порівнює кожен лівий рядок з кожним правим. Саме це найчастіше вбиває продуктивність LEFT JOIN у продакшені.
Типові помилки
Помилка 1: WHERE на стовпці правої таблиці перетворює LEFT JOIN на INNER JOIN.
-- БАГ: Bob і Carol зникають
SELECT c.name, p.item
FROM clients c
LEFT JOIN purchases p ON c.client_id = p.client_id
WHERE p.item IS NOT NULL;
-- ВИПРАВЛЕННЯ: перемісти умову в ON
SELECT c.name, p.item
FROM clients c
LEFT JOIN purchases p
ON c.client_id = p.client_id AND p.item IS NOT NULL;WHERE фільтрує після з'єднання і видаляє саме ті NULL-рядки, які ти хотів зберегти. Умова в ON застосовується під час з'єднання, тому ліві рядки без збігу все одно потрапляють у результат.
Помилка 2: Кілька збігів у правій таблиці множать рядки.
Якщо у клієнта 50 замовлень, у результаті буде 50 рядків для цього клієнта. Поведінка правильна, але вона дивує коли очікуєш один рядок на клієнта. Використовуй COUNT з GROUP BY.
SELECT c.name, COUNT(p.purchase_id) AS total_purchases
FROM clients c
LEFT JOIN purchases p ON c.client_id = p.client_id
GROUP BY c.client_id, c.name;
-- Alice | 2
-- Bob | 0
-- Carol | 0Помилка 3: Відсутній індекс на ключі з'єднання.
CREATE INDEX ON purchases(client_id);Без нього LEFT JOIN на таблиці з мільйоном рядків робить повний перебір при кожному запиті. Додай індекс один раз і різниця буде помітна одразу.
Де зустрічається
- E-commerce: всі користувачі LEFT JOIN замовлення, щоб знайти тих хто нічого не купував.
- SaaS-дашборди: всі користувачі LEFT JOIN події, щоб у звітах retention були видні і неактивні.
- CRM: всі ліди LEFT JOIN угоди, щоб pipeline показував ліди без прив'язаних угод.
- REST API:
users LEFT JOIN profilesколи профіль необов'язковий і є не у всіх користувачів.
Питання на співбесіді
Q: LEFT JOIN може повертати дублікати рядків?
A: Так. Якщо в правій таблиці кілька збігів, лівий рядок повторюється для кожного. Використовуй GROUP BY або агрегатні функції щоб згорнути їх.
Q: Що буде якщо ліва таблиця порожня?
A: Результат теж порожній. Немає лівих рядків що зберігати.
Q: LEFT JOIN повільніший за INNER JOIN?
A: Зазвичай трохи повільніший, бо рушій відстежує незбіглі ліві рядки. З індексами різниця мінімальна. Більше впливає розмір результату і наявність агрегації далі.
Q: Є ланцюжок: users LEFT JOIN orders LEFT JOIN items. Додаєш WHERE items.name = 'Laptop'. Що відбувається?
A: Весь результат поводиться як INNER JOIN. Користувачі без замовлень і замовлення без відповідних items зникають. Перемісти умову в ON другого join: ON orders.order_id = items.order_id AND items.name = 'Laptop'.
Приклади
Базовий: всі клієнти з опціональними покупками
-- Повертає всіх клієнтів; NULL там де покупок немає
SELECT c.name, p.item
FROM clients c
LEFT JOIN purchases p ON c.client_id = p.client_id;
-- Alice | Laptop
-- Alice | Mouse
-- Bob | NULL
-- Carol | NULLAlice з'являється двічі через дві покупки. Bob і Carol по одному разу з NULL в item.
Середній: статистика замовлень для дашборду
-- users: user_id | username
-- orders: order_id | user_id | total
SELECT
u.username,
COUNT(o.order_id) AS order_count,
COALESCE(AVG(o.total), 0) AS avg_order_value
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
GROUP BY u.user_id, u.username;
-- alice | 2 | 524.00
-- bob | 0 | 0.00COUNT(o.order_id) повертає 0 для bob, бо рахує тільки не-NULL значення. COALESCE(..., 0) перетворює NULL-середнє на 0. Цей паттерн трапляється майже на кожному SaaS-дашборді що відстежує активність по користувачах.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.