SQLLab
Все статьи

JOIN для начинающих: объединение таблиц простым языком

JOIN в SQL для начинающих: INNER JOIN, LEFT JOIN, RIGHT JOIN — объяснение на простых примерах. Когда использовать каждый тип, частые ошибки.

22 марта 2026 г.·4 мин чтения·

JOIN — один из ключевых навыков в SQL. С ним данные из нескольких таблиц объединяются в один результат. Разберём с нуля.

Зачем нужен JOIN

Данные в реляционной базе разбиты по разным таблицам. Пользователи — в users, заказы — в orders. Чтобы получить «заказы с именами пользователей» — нужен JOIN.

users                    orders
┌────┬──────┐           ┌────┬─────────┬────────┐
│ id │ name │           │ id │ user_id │ amount │
├────┼──────┤           ├────┼─────────┼────────┤
│  1 │ Анна │           │101 │    1    │  1500  │
│  2 │ Иван │           │102 │    1    │  2300  │
│  3 │ Маша │           │103 │    2    │   890  │
└────┴──────┘           └────┴─────────┴────────┘

Хотим: имя пользователя + сумма каждого заказа.

INNER JOIN — только совпадения

SELECT users.name, orders.amount
FROM users
INNER JOIN orders ON users.id = orders.user_id;

Результат:

nameamount
Анна1500
Анна2300
Иван890

Маша (id=3) не попала — у неё нет заказов. INNER JOIN возвращает только строки где есть совпадение в обеих таблицах.

LEFT JOIN — все строки из левой таблицы

SELECT users.name, orders.amount
FROM users
LEFT JOIN orders ON users.id = orders.user_id;

Результат:

nameamount
Анна1500
Анна2300
Иван890
МашаNULL

Маша есть в результате, но amount = NULL (нет заказов). LEFT JOIN возвращает все строки из левой таблицы (users), а для несовпадений — NULL из правой.

RIGHT JOIN — все строки из правой таблицы

Зеркало LEFT JOIN. Редко используется — обычно переписывают как LEFT JOIN меняя таблицы местами.

FULL OUTER JOIN — все строки из обеих таблиц

SELECT users.name, orders.amount
FROM users
FULL OUTER JOIN orders ON users.id = orders.user_id;

Все пользователи + все заказы. Несовпадения с обеих сторон заполняются NULL.

Псевдонимы таблиц

Для удобства таблицам дают короткие псевдонимы:

SELECT u.name, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

Несколько JOIN

-- Заказ → пользователь → товар
SELECT u.name, p.name AS product, oi.quantity
FROM orders o
JOIN users u         ON o.user_id    = u.id
JOIN order_items oi  ON o.id         = oi.order_id
JOIN products p      ON oi.product_id = p.id;

Каждый JOIN добавляет одну таблицу к результату.

JOIN + GROUP BY

Частый паттерн:

-- Количество заказов каждого пользователя
SELECT
    u.name,
    COUNT(o.id) AS orders_count,
    COALESCE(SUM(o.amount), 0) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name
ORDER BY total_spent DESC;

Найти строки без совпадения

-- Пользователи без ни одного заказа
SELECT u.name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;

После LEFT JOIN строки без совпадения имеют NULL в колонках правой таблицы — это и ловим.

Частые ошибки

INNER JOIN вместо LEFT JOIN — теряются строки:

-- Если хотим всех пользователей включая без заказов
-- INNER JOIN потеряет пользователей без заказов
-- Нужен LEFT JOIN

Дубли из-за JOIN один-ко-многим:

-- Если у пользователя 3 заказа — пользователь появится 3 раза
SELECT u.name FROM users u JOIN orders o ON u.id = o.user_id;

-- Если нужны уникальные пользователи:
SELECT DISTINCT u.name FROM users u JOIN orders o ON u.id = o.user_id;
-- или GROUP BY

Фильтр в WHERE вместо ON при LEFT JOIN:

-- Это эффективно превращает LEFT JOIN в INNER JOIN!
SELECT u.name FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.status = 'paid';  -- строки с NULL в o.status отфильтруются

-- Если хотим всех пользователей, но только paid-заказы:
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid'

Освоив INNER и LEFT JOIN, вы закрываете 95% задач с объединением таблиц.

Похожие статьи

Попробуй на практике

Тренажёр с реальными задачами — бесплатно и без регистрации

Открыть тренажёр →