Как объединить таблицы в SQL
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Два способа объединения
- JOIN — соединяет таблицы по условию (слева направо, добавляет колонки)
- UNION — складывает строки одной таблицы под другую (сверху вниз, те же колонки)
Чаще нужен JOIN. UNION — когда одинаковая структура, но данные из разных источников.
JOIN — соединение по условию
INNER JOIN
Только строки с совпадением в обеих таблицах:
SELECT u.name, o.total
FROM users u
INNER JOIN orders o ON o.user_id = u.id;Пользователи без заказов НЕ попадут. Заказы без пользователей тоже.
LEFT JOIN
Все строки из левой таблицы + совпавшие из правой:
SELECT u.name, o.total
FROM users u
LEFT JOIN orders o ON o.user_id = u.id;Пользователи без заказов попадут с NULL в total.
RIGHT JOIN
Наоборот — все из правой, совпавшие из левой. Редко используется (лучше перепишите в LEFT).
FULL OUTER JOIN
Все строки из обеих, NULL где нет пары:
SELECT u.name, o.total
FROM users u
FULL OUTER JOIN orders o ON o.user_id = u.id;CROSS JOIN
Декартово произведение (каждая с каждой):
SELECT u.name, c.country
FROM users u
CROSS JOIN countries c;Используется редко — обычно для генерации комбинаций.
Визуально
USERS ORDERS
id | name user_id | total
1 | Alice 1 | 100
2 | Bob 1 | 200
3 | Carol 4 | 50INNER JOIN
name | total
Alice | 100
Alice | 200
(Bob и Carol без заказов → не попадут, orders.user_id=4 тоже нет)LEFT JOIN
name | total
Alice | 100
Alice | 200
Bob | NULL ← без заказов, но попал
Carol | NULL1. JOIN двух таблиц
SELECT
u.id,
u.name,
u.email,
o.order_id,
o.total,
o.created_at
FROM users u
INNER JOIN orders o ON o.user_id = u.id;2. LEFT JOIN с агрегацией
Для каждого пользователя — его статистика (даже если заказов нет):
SELECT
u.id,
u.name,
COUNT(o.order_id) AS orders_cnt,
COALESCE(SUM(o.total), 0) AS total_spent
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.name;3. JOIN трёх таблиц
SELECT
u.name,
o.order_id,
p.product_name,
oi.quantity
FROM users u
JOIN orders o ON o.user_id = u.id
JOIN order_items oi ON oi.order_id = o.order_id
JOIN products p ON p.product_id = oi.product_id;4. JOIN с условиями в ON
-- только оплаченные заказы
SELECT u.name, o.total
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id
AND o.status = 'paid';Условие в ON (не в WHERE) — чтобы LEFT JOIN не превратился в INNER.
5. Анти-JOIN: пользователи БЕЗ заказов
SELECT u.*
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.order_id IS NULL;Или через NOT EXISTS:
SELECT u.*
FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);6. SELF JOIN
Таблица соединяется сама с собой:
-- находим пары сотрудников с одинаковым менеджером
SELECT
a.name AS emp1,
b.name AS emp2,
a.manager_id
FROM employees a
JOIN employees b
ON a.manager_id = b.manager_id
AND a.id < b.id;UNION — объединение строк
UNION vs UNION ALL
- UNION — удаляет дубликаты (медленнее)
- UNION ALL — сохраняет все (быстрее)
-- 2023-2026 — все заказы
SELECT * FROM orders_2023
UNION ALL
SELECT * FROM orders_2024
UNION ALL
SELECT * FROM orders_2025
UNION ALL
SELECT * FROM orders_2026;Правила:
- Одинаковое количество колонок
- Одинаковые типы (или совместимые)
- Порядок колонок имеет значение
7. UNION с пометкой источника
SELECT 'database_a' AS source, id, name FROM users_a
UNION ALL
SELECT 'database_b' AS source, id, name FROM users_b;8. UNION для вертикального анализа
Статистика разных таблиц в одном результате:
SELECT 'users' AS entity, COUNT(*) AS cnt FROM users
UNION ALL
SELECT 'orders', COUNT(*) FROM orders
UNION ALL
SELECT 'products', COUNT(*) FROM products;9. JOIN + подзапрос
Агрегируем в подзапросе, потом JOIN:
SELECT
u.name,
u.email,
us.orders_cnt,
us.total_spent
FROM users u
LEFT JOIN (
SELECT
user_id,
COUNT(*) AS orders_cnt,
SUM(total) AS total_spent
FROM orders
WHERE status = 'paid'
GROUP BY user_id
) us ON us.user_id = u.id;10. JOIN по нескольким колонкам
SELECT *
FROM orders o
JOIN order_status os
ON o.order_id = os.order_id
AND o.created_at >= os.valid_from
AND o.created_at < os.valid_to;Удобно для обработки версий / SCD Type 2.
Частые ошибки
Ошибка 1. INNER вместо LEFT
Теряем пользователей без заказов:
-- неверно, если нужны все юзеры
SELECT * FROM users JOIN orders ...
-- правильно
SELECT * FROM users LEFT JOIN orders ...Ошибка 2. Условие в WHERE вместо ON для LEFT JOIN
-- превращает LEFT в INNER
SELECT * FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.status = 'paid'; -- <- убирает строки с NULL
-- правильно
SELECT * FROM users u
LEFT JOIN orders o ON o.user_id = u.id AND o.status = 'paid';Ошибка 3. Cartesian explosion
-- users: 1000 строк, orders: 10000 строк
-- без ON или с неверным ON → 10 млн строк
SELECT * FROM users, orders;Ошибка 4. Дубликаты после JOIN
Если в правой таблице несколько строк на ключ — строка из левой размножается:
-- один user с 5 заказами → 5 строк
SELECT u.*, o.order_id FROM users u JOIN orders o ON o.user_id = u.idЕсли нужны uniq users → GROUP BY или DISTINCT.
Ошибка 5. Забыть алиасы
При самосоединении обязательны алиасы. Вообще — хорошая практика всегда.
Связанные темы
- JOIN SQL — шпаргалка
- LEFT JOIN vs INNER JOIN
- UNION vs UNION ALL
- Задачи на JOIN на собеседовании
- EXISTS SQL — шпаргалка
FAQ
JOIN или UNION?
JOIN — когда объединяете колонки из разных таблиц по условию. UNION — когда объединяете строки одинаковой структуры.
LEFT или INNER?
LEFT сохраняет всё из левой таблицы. INNER — только совпадения. Выбирайте по задаче.
UNION или UNION ALL?
UNION ALL быстрее (не дедуплицирует). Используйте его, если дубликаты не проблема или вы уверены, что их нет.
Можно ли JOIN 10 таблиц?
Технически да. Практически — выше 4-5 обычно страдает читаемость и производительность. Используйте CTE или temporary tables.
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.