ПОЛНОЕ РУКОВОДСТВО ПО JOIN В SQL
1. Что такое JOIN?
JOIN — это операция, которая объединяет строки из двух (или более) таблиц на основе общего поля (ключа).
Представьте, что у вас есть две таблицы:
Таблица «Клиенты» (customers):
customer_id |
name |
|---|---|
1 |
Иван |
2 |
Петр |
3 |
Анна |
Таблица «Заказы» (orders):
order_id |
customer_id |
product |
|---|---|---|
101 |
1 |
Ноутбук |
102 |
1 |
Телефон |
103 |
3 |
Планшет |
Теперь мы будем соединять их разными способами.
2. Диаграмма Венна — что это?
Диаграмма Венна — это графическое представление множеств и их пересечений. В контексте SQL она показывает, какие строки попадут в результат при разных типах JOIN.
Представьте два круга:
Левый круг — все строки из таблицы A (Customers)
Правый круг — все строки из таблицы B (Orders)
Пересечение — строки, которые есть в обеих таблицах (совпадение по ключу)
3. ВСЕ ТИПЫ JOIN (ПОДРОБНО)
3.1 INNER JOIN — Только пересечение
Диаграмма:
╭──────────╮
╱ A ╲
│ ╭──────╮ │
│ ╱ ✅ ╲ │ ← Только пересечение (совпадающие)
│ │ A∩B │ │
│ ╲ ╱ │
│ ╰──────╯ │
╲ ╱
╰──────────╯
Что делает: Возвращает только те строки, где есть совпадение в ОБЕИХ таблицах.
Пример 3.1.1 — INNER JOIN:
-- Клиенты, у которых есть заказы
SELECT
c.name,
o.product
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;
Результат:
name |
product |
|---|---|
Иван |
Ноутбук |
Иван |
Телефон |
Анна |
Планшет |
Почему нет Петра? У него нет заказов, поэтому он не попал в пересечение.
3.2 LEFT JOIN — Левая + пересечение
Диаграмма:
╭──────────╮
╱ A ╲
│ ╭──────╮ │
│ ╱ ✅ ╲ │ ← Все строки из A + пересечение
│ │ A∩B │ │
│ ╲ ╱ │
│ ╰──────╯ │
╲ ✅ ╱
╰──────────╯
Что делает: Возвращает ВСЕ строки из левой таблицы (A) + совпадающие из правой (B). Если совпадений нет — в колонках из правой таблицы будет NULL.
Пример 3.2.1 — LEFT JOIN:
-- Все клиенты и их заказы (даже без заказов)
SELECT
c.name,
o.product
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id;
Результат:
name |
product |
|---|---|
Иван |
Ноутбук |
Иван |
Телефон |
Петр |
NULL |
Анна |
Планшет |
Почему Петр есть? LEFT JOIN включает все строки из левой таблицы (customers), даже если нет совпадений.
3.3 RIGHT JOIN — Правая + пересечение
Диаграмма:
╭──────────╮
╱ B ╲
│ ╭──────╮ │
│ ╱ ✅ ╲ │ ← Все строки из B + пересечение
│ │ A∩B │ │
│ ╲ ╱ │
│ ╰──────╯ │
╲ ✅ ╱
╰──────────╯
Что делает: Возвращает ВСЕ строки из правой таблицы (B) + совпадающие из левой (A). Если совпадений нет — в колонках из левой таблицы будет NULL.
Пример 3.3.1 — RIGHT JOIN:
-- Все заказы и их клиенты (даже если клиент удален)
SELECT
c.name,
o.product
FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id;
Результат:
name |
product |
|---|---|
Иван |
Ноутбук |
Иван |
Телефон |
Анна |
Планшет |
Зачем нужен RIGHT JOIN? Обычно его используют, когда правая таблица — «главная» (например, мы хотим увидеть ВСЕ заказы, даже если клиент был удален из базы).
Важно: На практике
RIGHT JOINиспользуется редко. Его всегда можно заменить наLEFT JOIN, просто поменяв таблицы местами.
3.4 FULL OUTER JOIN — Все строки из обеих таблиц
Диаграмма:
╭──────────╮
╱ A ╱ ╲
│ ╭──────╮ │
│ ╱ ✅ ╲ │ ← ВСЕ строки из A И из B
│ │ A∪B │ │
│ ╲ ╱ │
│ ╰──────╯ │
╲ ╱ B ╱
╰──────────╯
Что делает: Возвращает ВСЕ строки из обеих таблиц. Если совпадения нет — недостающие колонки заполняются NULL.
Пример 3.4.1 — FULL OUTER JOIN:
-- Все клиенты и все заказы (полное объединение)
SELECT
c.name,
o.product
FROM customers c
FULL OUTER JOIN orders o ON c.customer_id = o.customer_id;
Результат:
name |
product |
|---|---|
Иван |
Ноутбук |
Иван |
Телефон |
Петр |
NULL |
Анна |
Планшет |
(В данном примере результат похож на LEFT JOIN, потому что у всех заказов есть клиенты. Если бы был заказ без клиента — он бы тоже появился.)
3.5 CROSS JOIN — Декартово произведение
Диаграмма:
╭──────────╮
╱ A ╲
│ ╭──────╮ │
│ ╱ ✅ ╲ │ ← КАЖДАЯ строка из A
│ │ A × B │ │ соединяется с КАЖДОЙ строкой из B
│ ╲ ╱ │
│ ╰──────╯ │
╲ B ╱
╰──────────╯
Что такое Декартово произведение?
Это все возможные комбинации строк из таблицы A и таблицы B.
Формула:
Количество строк результата = Количество строк в A × Количество строк в B
Пример 3.5.1 — CROSS JOIN:
-- Все комбинации клиентов и товаров (без учета реальных заказов)
SELECT
c.name,
p.product_name
FROM customers c
CROSS JOIN products p;
Результат (если у нас 3 клиента и 100 товаров):
name |
product_name |
|---|---|
Иван |
Ноутбук |
Иван |
Телефон |
Иван |
Планшет |
Петр |
Ноутбук |
Петр |
Телефон |
Петр |
Планшет |
Анна |
Ноутбук |
Анна |
Телефон |
Анна |
Планшет |
3 × 3 = 9 строк (в реальности 3 × 100 = 300 строк).
Где применяется CROSS JOIN?
Для генерации тестовых данных
Для создания «календарных сеток» (например, все комбинации магазин × день)
Для построения матриц (например, все возможные пары товаров для анализа корзины)
Внимание: CROSS JOIN может создать огромную таблицу! Если в A 1000 строк, а в B 1000 — результат будет 1 000 000 строк.
4. СРАВНИТЕЛЬНАЯ ТАБЛИЦА ВСЕХ JOIN
Тип JOIN |
Какие строки попадают |
Когда использовать |
|---|---|---|
INNER JOIN |
Только пересечение |
Когда нужны только данные, которые есть в обеих таблицах |
LEFT JOIN |
Вся левая + пересечение |
Когда нужно показать ВСЕ записи из основной таблицы, даже без данных в связанной |
RIGHT JOIN |
Вся правая + пересечение |
Когда нужно показать ВСЕ записи из связанной таблицы (редко) |
FULL OUTER JOIN |
Все строки из обеих |
Когда нужно ничего не потерять из обеих таблиц |
CROSS JOIN |
Каждая строка с каждой |
Для генерации комбинаций / матриц |
5. ВИЗУАЛЬНЫЙ ПРИМЕР (НА ДАННЫХ)
Таблицы:
employees:
employee_id |
name |
|---|---|
1 |
Иван |
2 |
Петр |
3 |
Анна |
departments:
dept_id |
dept_name |
employee_id |
|---|---|---|
101 |
Продажи |
1 |
102 |
Маркетинг |
1 |
103 |
IT |
3 |
104 |
Бухгалтерия |
NULL |
5.1 INNER JOIN
SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.employee_id = d.employee_id;
Результат: Иван-Продажи, Иван-Маркетинг, Анна-IT.
(Петр потерялся, Бухгалтерия потерялась)
5.2 LEFT JOIN
SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.employee_id = d.employee_id;
Результат: Иван-Продажи, Иван-Маркетинг, Анна-IT, Петр-NULL.
(Все сотрудники есть, даже без отдела)
5.3 RIGHT JOIN
SELECT e.name, d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.employee_id = d.employee_id;
Результат: Иван-Продажи, Иван-Маркетинг, Анна-IT, NULL-Бухгалтерия.
(Все отделы есть, даже без сотрудника)
5.4 FULL OUTER JOIN
SELECT e.name, d.dept_name
FROM employees e
FULL OUTER JOIN departments d ON e.employee_id = d.employee_id;
Результат: Иван-Продажи, Иван-Маркетинг, Анна-IT, Петр-NULL, NULL-Бухгалтерия.
(Ничего не потеряно)
5.5 CROSS JOIN
SELECT e.name, d.dept_name
FROM employees e
CROSS JOIN departments d;
Результат: 3 сотрудника × 4 отдела = 12 строк.
(Иван-Продажи, Иван-Маркетинг, Иван-IT, Иван-Бухгалтерия, Петр-Продажи, …)
6. ПОЧЕМУ ВАЖНО ПОНИМАТЬ JOIN?
Сценарий |
Какой JOIN использовать |
|---|---|
Отчет по продажам (только клиенты с покупками) |
|
Список всех клиентов (даже тех, кто ничего не купил) |
|
Анализ: какие товары НИКОГДА не продавались |
|
Поиск заказов без клиентов (ошибки в БД) |
|
Построение матрицы «клиент × товар» для анализа |
|
Сравнение двух таблиц (что есть в одной, но нет в другой) |
|
7. ШПАРГАЛКА (ЗАПОМНИТЬ НАВСЕГДА)
INNER JOIN = Только совпадения
LEFT JOIN = Все из левой + совпадения
RIGHT JOIN = Все из правой + совпадения
FULL JOIN = Все из левой И все из правой
CROSS JOIN = Каждый с каждым (Декартово произведение)
Мнемоническое правило:
INNER= «внутри» (только то, что внутри пересечения)LEFT= «слева все» (левая таблица важнее)RIGHT= «справа все» (правая таблица важнее)FULL= «все полностью» (обе таблицы важны)CROSS= «крест-накрест» (каждый с каждым)
8. ОШИБКИ НОВИЧКОВ
Ошибка 1: Использовать INNER JOIN, когда нужен LEFT
Плохо:
-- Потеряем клиентов без заказов
SELECT c.name, SUM(o.amount)
FROM customers c
JOIN orders o ON c.id = o.customer_id
GROUP BY c.name;
Хорошо:
-- Все клиенты (даже без заказов покажут 0)
SELECT c.name, COALESCE(SUM(o.amount), 0)
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.name;
Ошибка 2: Забыть про NULL в LEFT JOIN
-- Неправильно (потеряет клиентов без заказов)
SELECT * FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.amount > 1000;
Правильно:
-- Фильтруем после JOIN или используем INNER
SELECT * FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE o.amount > 1000 OR o.amount IS NULL;
Ошибка 3: Слишком много CROSS JOIN
-- Если в таблицах по 10 000 строк -> 100 000 000 строк!
SELECT * FROM big_table1 CROSS JOIN big_table2;
9. ПРАКТИЧЕСКОЕ ЗАДАНИЕ ДЛЯ СТУДЕНТА
На основе базы Northwind напишите:
INNER JOIN — список заказов с именем сотрудника (orders → employees)
LEFT JOIN — все товары и количество их продаж (products → order_details)
(товары без продаж должны показать NULL)LEFT JOIN + IS NULL — товары, которые никогда не продавались
FULL OUTER JOIN — сравнение: какие клиенты есть в customers, но не в orders, и наоборот
CROSS JOIN — все комбинации товаров и категорий (для проверки: сколько строк получится?)
Решение (для проверки):
-- 1. INNER JOIN
SELECT o.order_id, e.first_name || ' ' || e.last_name AS employee_name
FROM orders o
INNER JOIN employees e ON o.employee_id = e.employee_id;
-- 2. LEFT JOIN (все товары + продажи)
SELECT p.product_name, SUM(od.quantity) AS total_sold
FROM products p
LEFT JOIN order_details od ON p.product_id = od.product_id
GROUP BY p.product_id, p.product_name;
-- 3. Товары, которые никогда не продавались
SELECT p.product_name
FROM products p
LEFT JOIN order_details od ON p.product_id = od.product_id
WHERE od.product_id IS NULL;
-- 4. FULL OUTER JOIN (клиенты без заказов + заказы без клиентов)
SELECT
c.customer_id AS customer_id,
o.order_id AS order_id
FROM customers c
FULL OUTER JOIN orders o ON c.customer_id = o.customer_id
WHERE c.customer_id IS NULL OR o.order_id IS NULL;
-- 5. CROSS JOIN (количество строк = количество товаров × количество категорий)
SELECT p.product_name, c.category_name
FROM products p
CROSS JOIN categories c;
Теперь студент полностью понимает все типы JOIN, их диаграммы и практическое применение. Это знание проверяют на каждом собеседовании! 🎯