JOIN SQL: виды соединений, примеры и практика
JOIN SQL используется, чтобы объединять связанные данные из нескольких таблиц. На примерах проще понять разницу между INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN и CROSS JOIN и увидеть, почему после соединения количество строк может измениться.
Обновлено · Команда SQL Arena
Что потренируете
- INNER JOIN и условие соединения через ON
- LEFT JOIN и сохранение строк из левой таблицы
- FULL, CROSS и SELF JOIN
- антиджойн через LEFT JOIN и IS NULL
- агрегация после JOIN один-ко-многим
Пример задачи в тренажёре
Так выглядит задача в SQL Arena: условие, редактор, результат и схема базы.
Есть таблица отделов departments и таблица сотрудников employees. Нужно вывести каждый отдел, включая отделы без сотрудников. Для каждого отдела получите название в department_name, число сотрудников в employee_count и сумму их зарплат в payroll. Отдел без сотрудников должен получить 0 в обоих числовых столбцах.
SELECT d.department_name, COUNT(e.id) AS employee_count, COALESCE(SUM(e.salary), 0) AS payroll FROM departments AS d LEFT JOIN employees AS e ON e.department_id = d.id GROUP BY d.id, d.department_name ORDER BY d.id;
| # | department_name | employee_count | payroll |
|---|---|---|---|
| 1 | Editorial | 2 | 80000 |
| 2 | Design | 3 | 127000 |
| 3 | Research | 2 | 100000 |
| 4 | Support | 1 | 36000 |
Есть таблица отделов departments и таблица сотрудников employees. Нужно вывести каждый отдел, включая отделы без сотрудников. Для каждого отдела получите название в department_name, число сотрудников в employee_count и сумму их зарплат в payroll. Отдел без сотрудников должен получить 0 в обоих числовых столбцах.
SELECT d.department_name, COUNT(e.id) AS employee_count, COALESCE(SUM(e.salary), 0) AS payroll FROM departments AS d LEFT JOIN employees AS e ON e.department_id = d.id GROUP BY d.id, d.department_name ORDER BY d.id;
JOIN SQL на примере: пошаговый разбор LEFT JOIN
Начинаем с departments, потому что в результате нужны все отделы. Именно выбор левой таблицы определяет, что будет сохранено при LEFT JOIN. Условие e.department_id = d.id связывает дочернюю строку сотрудника с родительской строкой отдела. Для Editorial после соединения получится две строки, для Design — три, а для Archive останется одна строка, где поля из employees будут NULL.
Это важный момент в задачах на SQL JOIN: соединение один-ко-многим сначала размножает строки родительской таблицы, и лишь потом выполняется GROUP BY. Поэтому нельзя после JOIN бездумно суммировать, например, d.budget: бюджет Design повторился бы три раза — по одному разу для каждого сотрудника. В нашем запросе агрегируются только данные из дочерней таблицы: COUNT(e.id) считает сотрудников, а SUM(e.salary) складывает их зарплаты.
COUNT(e.id), а не COUNT(*), нужен ради отдела Archive. LEFT JOIN создаёт для него строку, поэтому COUNT(*) вернул бы 1. Значение e.id там NULL, а COUNT по колонке игнорирует NULL и правильно возвращает 0. SUM(e.salary) для пустого отдела даёт NULL, поэтому COALESCE(..., 0) превращает его в ноль. Группировка по d.id и названию собирает размноженные строки обратно по отделам, а ORDER BY d.id делает порядок результата однозначным.
Основные виды JOIN отличаются тем, какие строки сохраняются в результате. INNER JOIN возвращает только совпавшие строки из обеих таблиц. LEFT JOIN сохраняет все строки левой таблицы, даже если соответствия справа нет. RIGHT JOIN делает аналогичное для правой таблицы, а FULL JOIN сохраняет несовпавшие строки с обеих сторон. CROSS JOIN создаёт декартово произведение: каждая строка одной таблицы соединяется с каждой строкой другой. Поэтому перед написанием SQL JOIN важно сначала определить, какие строки должны остаться в результате.
SELECT d.department_name, COUNT(e.id) AS employee_count, COALESCE(SUM(e.salary), 0) AS payroll FROM departments AS d LEFT JOIN employees AS e ON e.department_id = d.id GROUP BY d.id, d.department_name ORDER BY d.id;
| department_name | employee_count | payroll |
|---|---|---|
| Editorial | 2 | 80000 |
| Design | 3 | 127000 |
| Research | 2 | 100000 |
| Support | 1 | 36000 |
| Archive | 0 | 0 |
Типичные ошибки в SQL запросах с JOIN
INNER JOIN вместо LEFT JOIN
Если заменить LEFT JOIN на INNER JOIN, отдел Archive исчезнет полностью: справа для него нет совпадающей строки. INNER JOIN подходит, когда нужны только пары, найденные с обеих сторон; LEFT JOIN — когда строки левой таблицы должны сохраниться даже без пары.
COUNT(*) после LEFT JOIN
В этой задаче COUNT(*) покажет для Archive одного сотрудника, хотя сотрудников там нет. Причина в том, что LEFT JOIN сохраняет строку отдела и подставляет NULL справа. Считать нужно колонку дочерней таблицы, которая заполнена только при реальном совпадении.
Агрегат по родительской таблице после JOIN 1:N
После соединения Design представлен тремя строками. Если в этот момент сделать SUM(d.budget), один и тот же бюджет попадёт в сумму трижды. Сначала нужно понять кардинальность связи и только потом выбирать, что и на каком уровне агрегировать.
Задачи по теме в тренажёре
Курс «SQL с нуля», глава про JOINПосле примера полезно решить задачи, где JOIN используется с другой целью. В бесплатных Easy-задачах можно, например, связать записи ошибок сервера, посчитать животных по секциям, найти города по завершённым операциям или вычислить свободную мощность дата-центров. Дальше встречаются задачи, где соединение сочетается с более сложной логикой и агрегацией. Всего в SQL Arena 1 192 задачи: у каждой задачи собственные таблицы, а решение проверяется на скрытых данных, а не сравнением текста запроса с шаблоном. В PostgreSQL, MySQL, ClickHouse и BigQuery есть отдельные песочницы прямо в браузере. Бесплатно без карты доступны 292 задачи на всех четырёх диалектах. Если JOIN пока приходится собирать методом проб, можно продолжить в курсе «SQL с нуля», в главе про JOIN: там теория сразу закрепляется задачами.
- EasyбесплатноОшибки сервера в nginxpostgresql
- EasyбесплатноКоличество животных в секции Africa Savannapostgresqlmysqlclickhouse
- EasyбесплатноТоп-3 города по завершённым сделкамpostgresqlmysqlclickhouse
- EasyбесплатноСвободная мощность дата-центровpostgresqlmysqlclickhouse
- MediumPremiumЧто сделал бы MERGEbigquery
- MediumPremiumСлияние без права записиbigquery
- MediumPremiumГонорары артистов за все выступленияpostgresqlmysqlclickhouse
- MediumPremiumСредняя длительность выступлений по жанрамpostgresqlmysqlclickhouse
Справочник по теме
Подробные статьи с синтаксисом и примерами в справочнике SQL Arena.
Частые вопросы
Что такое JOIN в SQL?
JOIN объединяет строки из двух или нескольких таблиц по заданному условию. Чаще всего связь задаётся через идентификаторы, например внешний ключ одной таблицы и первичный ключ другой.
Чем LEFT JOIN SQL отличается от INNER JOIN SQL?
INNER JOIN оставляет только строки, для которых найдено совпадение с обеих сторон. LEFT JOIN дополнительно сохраняет все строки левой таблицы, а отсутствующие значения правой стороны заполняются NULL.
Какие виды JOIN есть в SQL?
Основные виды — INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN и CROSS JOIN. Также встречаются SELF JOIN и антиджойн, хотя последний обычно выражается через LEFT JOIN с проверкой NULL или через NOT EXISTS.
Что делает CROSS JOIN SQL?
CROSS JOIN создаёт все возможные пары строк из двух наборов данных. Если в первой таблице 10 строк, а во второй 5, результат до дальнейшей фильтрации будет содержать 50 строк.
Почему после JOIN SQL становится больше строк?
Это происходит, когда одной строке одной таблицы соответствует несколько строк другой. При связи один-ко-многим исходная строка повторяется для каждого найденного соответствия.
Потренируйте JOIN SQL на практических задачах
В SQL Arena можно решать задачи на INNER, LEFT и другие виды JOIN в PostgreSQL, MySQL, ClickHouse и BigQuery. В редакторе доступны ER-диаграммы и EXPLAIN, а проверка запускается на скрытых данных. Для разбора ошибки можно обратиться к AI-ментору; бесплатно доступны 10 таких обращений всего.
Решать задачи на JOIN