Левое внешнее соединение: LEFT JOIN — получение всех записей из левой таблицы

Что такое LEFT JOIN и зачем он нужен? 🤔

Представьте ситуацию: у вас есть список сотрудников (employees) и список проектов (projects), над которыми они работают. Но не все сотрудники сейчас задействованы в проектах! Как получить полный список сотрудников, даже если у них нет активных задач? Вот тут на сцену выходит LEFT JOIN — ваш надежный инструмент для таких случаев.

LEFT JOIN (или левое внешнее соединение) — это оператор SQL, который:

  • Возвращает все записи из левой таблицы (первой в запросе)
  • И только соответствующие записи из правой таблицы
  • Если соответствий нет, вместо данных правой таблицы подставляются NULL
SELECT 
    employees.name,
    projects.title
FROM 
    employees
LEFT JOIN 
    projects ON employees.id = projects.employee_id;

Как работает LEFT JOIN под капотом? 🔧

Давайте разберём механизм работы на пальцах:

  1. Сначала SQL-движок берет каждую строку из левой таблицы (в нашем примере — employees)
  2. Ищет соответствующие строки в правой таблице (projects) по условию соединения
  3. Если находит — объединяет их в одну строку результата
  4. Если не находит — всё равно включает строку из левой таблицы, заполняя поля из правой NULL

Это принципиальное отличие от INNER JOIN, который вообще исключает строки без соответствий!

Практический пример: анализируем продажи 💼

Рассмотрим реалистичный сценарий из e-commerce:

SELECT 
    c.customer_name,
    o.order_date,
    o.order_amount
FROM 
    customers c
LEFT JOIN 
    orders o ON c.customer_id = o.customer_id
WHERE 
    c.registration_date > '2023-01-01';

Что мы здесь делаем:

  • Получаем всех новых клиентов (зарегистрированных после 1 января 2023)
  • Смотрим их заказы (если они есть)
  • Клиенты без заказов всё равно попадут в результат — это важно для маркетингового анализа!

Фишки и лайфхаки с LEFT JOIN 🛠️

Фильтрация по правой таблице — частое заблуждение! Этот запрос НЕ покажет клиентов без заказов:

-- Неправильно! Превращает LEFT JOIN в INNER JOIN
SELECT c.customer_name, o.order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_date > '2023-06-01';

Правильный подход — переносить условия в само соединение:

SELECT c.customer_name, o.order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id 
                   AND o.order_date > '2023-06-01';

Подсчёт несоответствий — мощный аналитический приём:

SELECT 
    COUNT(*) AS total_customers,
    COUNT(o.order_id) AS customers_with_orders,
    COUNT(*) - COUNT(o.order_id) AS customers_without_orders
FROM 
    customers c
LEFT JOIN 
    orders o ON c.customer_id = o.customer_id;

Когда использовать LEFT JOIN? 🎯

Основные сценарии применения:

  • Отчёты, где важна полнота данных из главной таблицы
  • Поиск "дыр" в данных (например, товары без продаж)
  • Анализ эффективности (клиенты без покупок, сотрудники без задач)
  • Построение дашбордов с сохранением всех категорий

Частые ошибки и как их избежать 🚨

  1. Путаница с порядком таблиц — помните, что LEFT JOIN сохраняет строки из таблицы слева!
  2. WHERE вместо AND — как мы уже разобрали, это меняет логику работы
  3. Избыточные данные — при соединении с дублирующимися записями справа левая строка может "размножиться"

Проверочный чек-лист перед использованием:

✅ Определил, какая таблица должна быть "главной" (левой)? ✅ Все ли нужные условия перенесены в ON вместо WHERE? ✅ Проверил, не дублируются ли строки из-за правой таблицы?

Упражнение для закрепления 🏋️‍♂️

Попробуйте решить задачу: для каждого отдела компании вывести количество сотрудников, включая отделы без сотрудников.

Исходные таблицы:

CREATE TABLE departments (
    id INT PRIMARY KEY,
    name VARCHAR(100)
);

CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    department_id INT REFERENCES departments(id)
);

Решение:

SELECT 
    d.name AS department_name,
    COUNT(e.id) AS employees_count
FROM 
    departments d
LEFT JOIN 
    employees e ON d.id = e.department_id
GROUP BY 
    d.name;

Обратите внимание: мы используем COUNT(e.id) вместо COUNT(*), чтобы правильно учесть отделы без сотрудников (иначе получим 1 вместо 0).

Продвинутые техники 🚀

LEFT JOIN можно комбинировать с другими операциями:

Каскадные соединения:

SELECT 
    u.name,
    p.post_title,
    c.comment_text
FROM 
    users u
LEFT JOIN 
    posts p ON u.id = p.user_id
LEFT JOIN 
    comments c ON p.id = c.post_id;

С агрегатными функциями:

SELECT 
    d.department_name,
    AVG(e.salary) AS avg_salary
FROM 
    departments d
LEFT JOIN 
    employees e ON d.id = e.department_id
GROUP BY 
    d.department_name;

Для поиска отсутствующих записей:

SELECT 
    p.product_name
FROM 
    products p
LEFT JOIN 
    order_items oi ON p.id = oi.product_id
WHERE 
    oi.product_id IS NULL;

LEFT JOIN — ваш верный помощник в ситуациях, когда важно сохранить все записи основной таблицы, даже если к ним нет соответствий. Освоив эту технику, вы сможете создавать более полные и точные аналитические отчёты, находить пробелы в данных и строить действительно информативные SQL-запросы! 💪

Скрыть рекламу навсегда

📘 VK Видео — обучение без ограничений

Все уроки доступны без VPN, без блокировок и зависаний.

Можно смотреть с телефона, планшета или компьютера — в любое время.

▶️ Смотреть на VK Видео