Наглядный гайд по SQL JOIN: шпаргалка с примерами, нюансами NULL и планами запросов
Подробный разбор INNER, LEFT, RIGHT, FULL и CROSS JOIN. Разбираемся, как базы данных выполняют соединения под капотом (Nested Loop, Hash Join, Merge Join).
Вопрос о типах объединения таблиц (JOIN) — абсолютная классика любого технического интервью для бэкендеров, системных аналитиков, тестировщиков и дата-инженеров.
На собеседовании кандидату редко дают идеальные данные: почти всегда добавляют NULL, дубликаты ключей и просят объяснить, как движок СУБД (PostgreSQL или MySQL) будет выполнять этот запрос на миллионах строк.
В этой статье мы структурируем все виды JOIN на наглядных данных, разберем коварные ловушки с NULL и заглянем под капот алгоритмов соединения.
Исходные данные для примеров#
Для демонстрации возьмем две простые таблицы:
Таблица developers (Разработчики):
| id | name | team_id |
|---|---|---|
| 1 | Антон | 10 |
| 2 | Лена | 20 |
| 3 | Денис | NULL |
Таблица teams (Команды):
| id | title |
|---|---|
| 10 | Backend |
| 20 | Frontend |
| 30 | DevOps |
Обратите внимание: разработчик Денис ещё не распределен в команду (team_id = NULL), а в команде DevOps пока нет ни одного сотрудника.
1. INNER JOIN (Внутреннее соединение)#
Возвращает строки только тогда, когда условие соединения истинно для обеих таблиц одновременно.
SELECT
d.name,
t.title AS team_name
FROM developers d
INNER JOIN teams t ON d.team_id = t.id;
Результат:#
| name | team_name |
|---|---|
| Антон | Backend |
| Лена | Frontend |
- Денис отфильтрован, так как
NULL = 10даетUNKNOWN. - Команда DevOps отфильтрована, так как на неё никто не ссылается.
2. LEFT OUTER JOIN (Левое соединение)#
Возвращает все строки из левой таблицы, дополняя их данными из правой таблицы. Если совпадения нет — поля правой таблицы заполняются NULL.
SELECT
d.name,
t.title AS team_name
FROM developers d
LEFT JOIN teams t ON d.team_id = t.id;
Результат:#
| name | team_name |
|---|---|
| Антон | Backend |
| Лена | Frontend |
| Денис | NULL |
Вопрос: Как найти всех разработчиков, у которых нет команды?
Решение: Используйте LEFT JOIN с фильтрацией WHERE t.id IS NULL:
SELECT d.name
FROM developers d
LEFT JOIN teams t ON d.team_id = t.id
WHERE t.id IS NULL;
3. FULL OUTER JOIN (Полное внешнее соединение)#
Объединяет результаты LEFT и RIGHT JOIN. В результирующую выборку попадают все строки из обеих таблиц. Если совпадения нет с любой из сторон — подставляется NULL.
SELECT
d.name,
t.title AS team_name
FROM developers d
FULL OUTER JOIN teams t ON d.team_id = t.id;
Результат:#
| name | team_name |
|---|---|
| Антон | Backend |
| Лена | Frontend |
| Денис | NULL |
| NULL | DevOps |
4. CROSS JOIN (Декартово произведение)#
Соединяет каждую строку первой таблицы с каждой строкой второй таблицы. Количество строк в результате равно произведению строк исходных таблиц ($3 \times 3 = 9$).
SELECT d.name, t.title
FROM developers d
CROSS JOIN teams t;
Случайный CROSS JOIN (или забытое условие ON в устаревшем синтаксисе FROM table1, table2) на таблицах по 100 000 строк породит выборку из 10 миллиардов строк и положит память сервера БД.
5. Как СУБД выполняет JOIN под капотом?#
На Middle+ собеседовании вас обязательно спросят: «Как планировщик PostgreSQL выбирает алгоритм соединения?».
Существует три основных алгоритма:
- Nested Loop (Вложенные циклы):
- Для каждой строки внешней таблицы ищется совпадение во внутренней таблице.
- Идеален, когда одна из таблиц очень мала, либо для второй таблицы есть индекс по внешнему ключу.
- Hash Join (Хеш-соединение):
- СУБД строит в оперативной памяти хеш-таблицу по меньшей таблице, затем сканирует вторую таблицу и проверяет наличие ключей в хеше.
- Используется для больших неотсортированных объемов данных при соединении по равенству (
=).
- Merge Join (Соединение слиянием):
- Обе таблицы сортируются по ключу соединения, после чего движок параллельно идет по двум отсортированным спискам.
- Самый быстрый алгоритм для огромных объемов, если данные уже отсортированы (например, по индексу B-Tree).
Сводная таблица типов соединений#
| Тип JOIN | Что попадает в результат | Что будет при отсутствии пары |
|---|---|---|
INNER | Только совпадения с обеих сторон | Строка отбрасывается |
LEFT | Все из левой таблицы + совпадения справа | Поля справа = NULL |
RIGHT | Все из правой таблицы + совпадения слева | Поля слева = NULL |
FULL | Все строки из обеих таблиц | NULL на несовпадающей стороне |
CROSS | Каждая строка с каждой (декартово произведение) | — |
Резюме#
Понимание физических планов (EXPLAIN ANALYZE) и поведения NULL при соединениях таблиц — ключевой навык для проектирования быстрых запросов в реляционных базах данных.
Прокачивай IT-скиллы с Capycodio
Не зубри теорию часами перед компьютером. Короткие 3-минутные интерактивные сессии прямо с телефона: по дороге, за кофе или перед сном.
Мы создаем интерактивный мобильный тренажер для разработчиков. Короткие сессии по 3-5 минут, чтобы держать базу и закрывать пробелы перед собеседованиями.