Сравнение: Типы JOIN в SQL и когда их применять

GuDron

dumpz.ws
Admin
Регистрация
28 Янв 2020
Сообщения
10,131
Реакции
1,573
Credits
36,282
Сравнение: Типы JOIN в SQL и когда их применять
Зачем понимать JOIN’ы?
Правильный выбор типа соединения таблиц позволяет получать необходимые данные эффективно и избегать неожиданных «пустых» или дублирующихся строк.

1. Основные типы JOIN и их поведение
INNER JOIN - Возвращает только строки, у которых есть совпадения в обеих таблицах. Когда нужно только пересечение данных.
LEFT JOIN - Берёт все строки из левой таблицы и совпадающие из правой (NULL, если нет). Когда важно сохранить все данные «слева» даже без пары.
RIGHT JOIN - Аналог LEFT, но берёт все из правой таблицы. Редко используется, чаще удобнее поменять местами таблицы.
FULL JOIN - Объединяет LEFT и RIGHT: все строки из обеих таблиц, NULL там, где нет пары. Когда нужны все данные из обеих, и нет явного «лево/право».
CROSS JOIN - Декартово произведение: каждая строка левой с каждой строкой правой. При генерации матриц или тестовых наборов.

2. Примеры синтаксиса
SQL:
-- INNER: только общие заказы и клиенты
SELECT o.id, c.name
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id;

-- LEFT: все заказы, даже если клиента нет (NULL)
SELECT o.id, c.name
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id;

-- FULL: все заказы и все клиенты
SELECT o.id AS order_id, c.id AS customer_id
FROM orders o
FULL JOIN customers c ON o.customer_id = c.id;

3. Лучшие практики и советы
1. Всегда уточняйте направление соединения
Понимайте, какая таблица «левее»: от этого зависит полнота результатов.
2. Используйте явный JOIN вместо «старого» синтаксиса через WHERE
Повышает читабельность и уменьшает риск ошибок.
3. Ограничивайте выборку
Добавляйте фильтры (WHERE, ON) до JOIN, чтобы не нагружать соединение лишними данными.
4. Проверяйте результаты на NULL
При LEFT/FULL JOIN обрабатывайте NULL через COALESCE или дополнительные условия.


4. Подводные камни
* Нежелательный CROSS JOIN
Пропущенный условный оператор соединения приведёт к взрывному росту строк.
* Производительность
JOIN’ы на больших таблицах без индексов по ключам могут быть медленными.
* Дублирование
Многократное соединение одной таблицы без корректных условий — источник «дублей».


Вывод: понимание семантики JOIN’ов — ключ к точной и быстрой выборке данных.
 

GuDron

dumpz.ws
Admin
Регистрация
28 Янв 2020
Сообщения
10,131
Реакции
1,573
Credits
36,282
Типы JOIN в SQL и когда их применять

- INNER JOIN
- пересечение множеств (только совпавшие строки).
- LEFT JOIN - все слева + совпавшие справа (несовпавшие → NULL).
- RIGHT JOIN - симметричен LEFT, лучше переворачивать под LEFT.
- FULL OUTER JOIN - все слева и справа (где нет пары → NULL).
- CROSS JOIN — декартово произведение (каждая со всеми).
- SELF JOIN - таблица соединяется сама с собой.
- SEMI / ANTI JOIN - “есть/нет соответствия” (через EXISTS / NOT EXISTS).
- LATERAL / APPLY - зависимая подзапросная таблица на строку слева.


1️⃣ INNER JOIN - «строго есть пара»
«Покажи оплаченные заказы с данными клиента».
SQL:
SELECT o.id, c.name
FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.status = 'paid';
Используйте, когда отсутствие пары - повод исключить строку.

2️⃣ LEFT JOIN - «все слева, даже без пары»
«Список клиентов и количество их заказов (включая с нулём)».
SQL:
SELECT c.id, c.name, COALESCE(COUNT(o.id), 0) AS orders_cnt
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.id, c.name;
По умолчанию для «обогащения» справочниками и опциональных связей.

3️⃣ RIGHT JOIN - почти не нужен
Заменяйте на LEFT, поменяв стороны:
SQL:
-- было:
-- SELECT ... FROM A RIGHT JOIN B ON ...
-- стало:
SELECT ...
FROM B
LEFT JOIN A ON ...

4️⃣ FULL OUTER JOIN - «объединить всё»
«Свод по всем клиентам и всем заказам, даже если без пары».
SQL:
SELECT COALESCE(c.id, o.customer_id) AS customer_key, c.name, o.id AS order_id
FROM customers c
FULL JOIN orders o ON o.customer_id = c.id;
Редко нужен в отчётах/сверках. Поддержка зависит от СУБД.

5️⃣ CROSS JOIN - «все комбинации»
«Собрать сетку метрик по всем регионам и кварталам».
SQL:
SELECT r.region, q.quarter
FROM regions r
CROSS JOIN quarters q;
Осторожно: взрыв строк.

6️⃣ SELF JOIN - «сравнить строки внутри таблицы»
«Найти менеджера и его подчинённого».
SQL:
SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON m.id = e.manager_id;

7️⃣ SEMI JOIN (EXISTS) - «фильтрация по факту наличия»
«Клиенты, у кого были заказы за 30 дней».
SQL:
SELECT c.*
FROM customers c
WHERE EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.id
    AND o.created_at >= CURRENT_DATE - INTERVAL '30 day'
);
Не размножает строки, часто быстрее, чем JOIN + DISTINCT.

8️⃣ ANTI JOIN (NOT EXISTS) - «кто без соответствий»
«Товары, которые ни разу не покупали в этом году».
SQL:
SELECT p.*
FROM products p
WHERE NOT EXISTS (
  SELECT 1 FROM order_items oi
  JOIN orders o ON o.id = oi.order_id
  WHERE oi.product_id = p.id
    AND o.created_at >= date_trunc('year', CURRENT_DATE)
);
Избегайте NOT IN с NULL - может дать пустой результат.

9️⃣ LATERAL / APPLY - «топ-N на строку»
«Последний заказ на клиента» (PostgreSQL: LATERAL, SQL Server: APPLY).
SQL:
SELECT c.id, c.name, o_last.id AS last_order_id
FROM customers c
LEFT JOIN LATERAL (
  SELECT o.id
  FROM orders o
  WHERE o.customer_id = c.id
  ORDER BY o.created_at DESC
  LIMIT 1
) o_last ON true;

Подводные камни

- LEFT JOIN + фильтр в WHERE ⇒ превращается в INNER.

Если нужно оставить «без пары», переносите условие в ON:
SQL:
-- ❌ неверно
SELECT ... FROM c LEFT JOIN o ON o.customer_id = c.id
WHERE o.status = 'paid';

-- ✅ верно
SELECT ... FROM c LEFT JOIN o
ON o.customer_id = c.id AND o.status = 'paid';

- Дубликаты из «один-ко-многим». Перед JOIN делайте агрегацию в подзапросе/CTE.
- Индексы на ключах соединений (FK и соответствующие PK/UK) - must.
- Сопоставимость типов/колляций. Функции на ключе (LOWER(col)) ломают sargability - лучше нормализовать данные заранее.
- EXISTS чаще лучше, чем JOIN + DISTINCT для фильтрации.
- Проверяйте план. EXPLAIN (ANALYZE, BUFFERS) и сравнение альтернатив.