Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як працює left join?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**LEFT JOIN** повертає всі рядки з лівої таблиці та відповідні рядки з правої. Де збігу немає, праві стовпці заповнюються NULL. ```sql SELECT c.name, p.item FROM clients c LEFT JOIN purchases p ON c.client_id = p.client_id; -- Bob | NULL (покупок немає, але рядок є) ``` **Ключове:** кожен лівий рядок завжди є в результаті, незалежно від збігу.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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-дашборді що відстежує активність по користувачах.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.