ПОЛНОЕ РУКОВОДСТВО ПО 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 использовать

Отчет по продажам (только клиенты с покупками)

INNER JOIN

Список всех клиентов (даже тех, кто ничего не купил)

LEFT JOIN

Анализ: какие товары НИКОГДА не продавались

LEFT JOIN + WHERE right.id IS NULL

Поиск заказов без клиентов (ошибки в БД)

RIGHT JOIN или FULL OUTER JOIN

Построение матрицы «клиент × товар» для анализа

CROSS JOIN

Сравнение двух таблиц (что есть в одной, но нет в другой)

FULL OUTER JOIN + WHERE


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 напишите:

  1. INNER JOIN — список заказов с именем сотрудника (orders → employees)

  2. LEFT JOIN — все товары и количество их продаж (products → order_details)
    (товары без продаж должны показать NULL)

  3. LEFT JOIN + IS NULL — товары, которые никогда не продавались

  4. FULL OUTER JOIN — сравнение: какие клиенты есть в customers, но не в orders, и наоборот

  5. 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, их диаграммы и практическое применение. Это знание проверяют на каждом собеседовании! 🎯