UNION в SQL — как объединять результаты запросов
Что делает UNION
UNION берёт результаты двух SELECT-запросов и склеивает их вертикально — строки одного запроса добавляются к строкам другого.
Представьте две таблицы: orders_2024 и orders_2025. Нужно получить список всех клиентов за оба года. Вместо двух отдельных запросов — один, с UNION:
SELECT client_id, client_name FROM orders_2024
UNION
SELECT client_id, client_name FROM orders_2025;Результат — единый список клиентов без дубликатов. Если клиент покупал в оба года, он появится один раз.
UNION vs UNION ALL
Главный вопрос на любом собеседовании по SQL: чем отличается UNION от UNION ALL?
UNION — объединяет и удаляет дубликаты. Под капотом делает DISTINCT по всем колонкам результата.
UNION ALL — объединяет и оставляет все строки как есть. Никакой дедупликации.
-- UNION: 'Москва' будет один раз
SELECT city FROM customers
UNION
SELECT city FROM suppliers;
-- UNION ALL: 'Москва' будет столько раз,
-- сколько встречается в обеих таблицах суммарно
SELECT city FROM customers
UNION ALL
SELECT city FROM suppliers;Производительность. UNION ALL всегда быстрее — не тратит ресурсы на сортировку и дедупликацию. На миллионе строк разница ощутимая.
Правило простое: используйте UNION ALL по умолчанию. UNION — только когда дубликаты действительно нужно убрать. Подробнее — в сравнении UNION vs UNION ALL.
Правила объединения
Чтобы UNION (или UNION ALL) сработал, оба SELECT должны соблюдать два условия:
- Одинаковое количество колонок. Если первый SELECT возвращает три колонки, второй тоже должен вернуть три.
- Совместимые типы данных. Первая колонка первого запроса сопоставляется с первой колонкой второго, вторая со второй и т.д. Типы должны быть совместимы (INT и BIGINT — ок, INT и VARCHAR — ошибка).
-- Ошибка: разное количество колонок
SELECT name, city, age FROM customers
UNION ALL
SELECT name, city FROM suppliers;Названия колонок берутся из первого SELECT. Если в первом запросе колонка называется client_name, а во втором supplier_name — в результате будет client_name.
ORDER BY с UNION
ORDER BY можно использовать только один раз — в самом конце, после последнего SELECT. Он сортирует весь объединённый результат.
SELECT product_name, revenue FROM sales_q1
UNION ALL
SELECT product_name, revenue FROM sales_q2
ORDER BY revenue DESC;Поставить ORDER BY внутри отдельного SELECT перед UNION нельзя — будет синтаксическая ошибка. Если нужно ограничить строки внутри каждого запроса, оберните его в подзапрос:
SELECT * FROM (
SELECT product_name, revenue FROM sales_q1 ORDER BY revenue DESC LIMIT 5
) top_q1
UNION ALL
SELECT * FROM (
SELECT product_name, revenue FROM sales_q2 ORDER BY revenue DESC LIMIT 5
) top_q2;Практические примеры
Объединение данных из разных периодов
Частая задача — данные разбиты по таблицам (партиционирование по годам, месяцам):
SELECT user_id, event_type, created_at FROM events_2024
UNION ALL
SELECT user_id, event_type, created_at FROM events_2025
ORDER BY created_at;Создание справочной таблицы на лету
Иногда нужен маппинг, которого нет в базе:
SELECT 1 AS month_num, 'Январь' AS month_name
UNION ALL SELECT 2, 'Февраль'
UNION ALL SELECT 3, 'Март'
UNION ALL SELECT 4, 'Апрель';Результат можно использовать как CTE или подзапрос для джойна.
Сборка событий из разных таблиц
Классическая задача аналитика — собрать единый лог действий пользователя:
SELECT user_id, 'purchase' AS event, created_at FROM purchases
UNION ALL
SELECT user_id, 'login' AS event, created_at FROM logins
UNION ALL
SELECT user_id, 'signup' AS event, created_at FROM registrations
ORDER BY user_id, created_at;Здесь UNION ALL — единственный правильный выбор: данные из разных таблиц, дубликатов быть не может.
UNION vs JOIN
На собеседованиях часто проверяют, понимает ли кандидат разницу.
UNION — вертикальное объединение. Добавляет строки. Оба запроса должны иметь одинаковую структуру колонок.
JOIN — горизонтальное объединение. Добавляет колонки. Таблицы связываются по ключу.
-- UNION: «дай мне всех клиентов из двух источников»
SELECT name FROM customers_online
UNION
SELECT name FROM customers_offline;
-- JOIN: «дай мне клиентов с их заказами»
SELECT c.name, o.amount
FROM customers c
JOIN orders o ON c.id = o.customer_id;Если задача — «объединить данные из двух таблиц», уточните, что именно нужно: добавить строки (UNION) или добавить колонки (JOIN). Это две совершенно разные операции.
EXCEPT и INTERSECT
Рядом с UNION живут ещё два оператора множеств:
EXCEPT — строки из первого запроса, которых нет во втором:
SELECT user_id FROM users_2024
EXCEPT
SELECT user_id FROM users_2025;
-- Пользователи, которые были в 2024, но ушли в 2025INTERSECT — строки, которые есть в обоих запросах:
SELECT user_id FROM users_2024
INTERSECT
SELECT user_id FROM users_2025;
-- Пользователи, которые остались с 2024 по 2025Оба оператора, как и UNION, убирают дубликаты. Правила по количеству колонок и типам — те же.
Частые ошибки
- Разное количество колонок. Забыли добавить колонку в один из SELECT — получите ошибку. Решение: перечислите колонки явно, не используйте SELECT *.
- ORDER BY в середине. ORDER BY работает только после последнего SELECT. Ставить его между UNION — синтаксическая ошибка.
- UNION вместо JOIN. Если нужно «добавить данные из другой таблицы по ключу» — это JOIN, не UNION.
- UNION вместо UNION ALL. Бессмысленная дедупликация замедляет запрос. Если дубликатов быть не может — используйте UNION ALL.
Вопросы с собеседований
1. Чем отличается UNION от UNION ALL?
UNION объединяет результаты двух запросов и удаляет дублирующиеся строки — фактически выполняет DISTINCT по всему результату. UNION ALL объединяет без дедупликации, сохраняя все строки. UNION ALL быстрее, потому что не требует сортировки.
2. Можно ли использовать ORDER BY внутри UNION?
Нет. ORDER BY применяется только к финальному результату — после последнего SELECT. Если нужно ограничить строки внутри отдельного запроса, оберните его в подзапрос с ORDER BY и LIMIT.
3. Чем UNION отличается от JOIN?
UNION складывает строки вертикально: результат первого запроса + результат второго. JOIN соединяет таблицы горизонтально — добавляет колонки по ключу связи. UNION требует одинаковое число колонок, JOIN — наличие ключа для связи.
4. Напишите запрос: клиенты, которые делали покупки и в онлайне, и в офлайне.
SELECT customer_id FROM online_orders
INTERSECT
SELECT customer_id FROM offline_orders;INTERSECT возвращает только те строки, которые присутствуют в обоих результатах — то есть клиентов, которые покупали через оба канала.
5. Как объединить данные из трёх таблиц с разной структурой?
Нужно привести SELECT-запросы к одинаковой структуре — добавить недостающие колонки с NULL или константами:
SELECT user_id, amount, 'purchase' AS type FROM purchases
UNION ALL
SELECT user_id, NULL AS amount, 'login' AS type FROM logins
UNION ALL
SELECT user_id, NULL AS amount, 'signup' AS type FROM signups;Как тренироваться
UNION — одна из базовых тем SQL, которую спрашивают на собеседованиях любого уровня. Звучит просто, но в связке с GROUP BY, подзапросами и оконными функциями задачи становятся нетривиальными.
В тренажёре Карьерник есть задачи на объединение запросов, операции с множествами и комбинированные задачи, где UNION нужен вместе с JOIN и агрегатами. 15 минут в день — и на собесе эта тема не вызовет затруднений.
Ещё по SQL: шпаргалка по JOIN, UNION vs UNION ALL — детальное сравнение, 1500+ примеров вопросов.
FAQ
Сколько запросов можно объединить через UNION?
Ограничений нет — цепочка может быть любой длины: SELECT ... UNION ALL SELECT ... UNION ALL SELECT ... . На практике больше 5–7 встречается редко, но технически лимита нет.
UNION сохраняет порядок строк?
Нет. Без ORDER BY порядок строк в результате не гарантирован. Если порядок важен — всегда добавляйте ORDER BY в конце.
Можно ли использовать UNION с INSERT?
Да. INSERT INTO ... SELECT ... UNION ALL SELECT ... — допустимая конструкция. Это удобно для заполнения таблицы данными из нескольких источников одним запросом.
Работает ли UNION с GROUP BY?
Да. GROUP BY применяется внутри каждого отдельного SELECT, а UNION объединяет уже агрегированные результаты. Это типичный паттерн — агрегировать данные из разных таблиц и склеить результаты.