Skip to main content

Як працює left join?

LEFT JOIN повертає всі рядки з лівої таблиці плюс відповідні рядки з правої. Де збігу немає, стовпці правої таблиці заповнюються NULL.

Теорія

TL;DR

  • Уяви, що ліва таблиця це список запрошених на вечірку. Кожен зі списку потрапить у результат. Права таблиця це замовлення їжі. Немає замовлення = порожня тарілка (NULL).
  • Кожен рядок лівої таблиці з'явиться в результаті мінімум один раз. Незбіглі праві рядки просто відкидаються.
  • Використовуй LEFT JOIN коли потрібні всі записи зліва, навіть якщо праворуч немає збігу.

Швидкий приклад

sql
-- таблиця 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.

sql
-- БАГ: 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.

sql
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: Відсутній індекс на ключі з'єднання.

sql
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'.

Приклади

Базовий: всі клієнти з опціональними покупками

sql
-- Повертає всіх клієнтів; 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 | NULL

Alice з'являється двічі через дві покупки. Bob і Carol по одному разу з NULL в item.

Середній: статистика замовлень для дашборду

sql
-- 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.00

COUNT(o.order_id) повертає 0 для bob, бо рахує тільки не-NULL значення. COALESCE(..., 0) перетворює NULL-середнє на 0. Цей паттерн трапляється майже на кожному SaaS-дашборді що відстежує активність по користувачах.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?