UNION SQL: шпаргалка для собеседования

Проверь себя · 1/3разбор после ответа
Нужно получить 5 самых дешёвых товаров категории 'electronics' из таблицы products. Какой запрос верный?

Зачем аналитику UNION

UNION объединяет результаты двух SELECT по строкам (склейка vertical). Нужен, когда хочешь смешать данные из разных таблиц или сценариев в один результирующий набор.

Базовый синтаксис

SELECT col1, col2 FROM table_a
UNION
SELECT col1, col2 FROM table_b;

Обязательно:

  • Одинаковое количество столбцов в обоих SELECT.
  • Совместимые типы в каждой позиции.
  • Имена столбцов берутся из первого SELECT.

UNION vs UNION ALL

-- UNION удаляет дубликаты (медленнее)
SELECT 1 UNION SELECT 1 UNION SELECT 2;
-- Результат: 1, 2

-- UNION ALL оставляет все (быстрее)
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 2;
-- Результат: 1, 1, 2

Главное правило: если дубликатов заведомо нет — используй UNION ALL. Он быстрее, потому что не делает внутреннюю дедупликацию.

На собесе: «Когда использовать UNION vs UNION ALL?» Ответ: UNION ALL когда уверен, что дублей не будет (например, непересекающиеся условия). UNION — когда нужна дедупликация.

INTERSECT и EXCEPT

Родственники UNION, используются реже, но спрашивают.

INTERSECT — пересечение

SELECT user_id FROM orders_2025
INTERSECT
SELECT user_id FROM orders_2026;
-- Пользователи, которые покупали и в 2025 и в 2026

EXCEPT (или MINUS в Oracle)

SELECT user_id FROM users
EXCEPT
SELECT user_id FROM orders;
-- Пользователи без заказов (аналог LEFT JOIN + IS NULL)

Тренироваться на таких вопросах можно в Telegram-боте Карьерник — там 1500+ задач с реальных собесов с разборами.

Порядок столбцов имеет значение

-- ❌ Типы не совпадают — ошибка
SELECT 'Ivan', 25 UNION SELECT 30, 'Petr';

-- ✅ Порядок должен соответствовать
SELECT 'Ivan', 25 UNION SELECT 'Petr', 30;

Проверяется по позиции, не по имени — это частый источник багов.

Имена столбцов

SELECT name AS user_name FROM users_a
UNION
SELECT full_name FROM users_b;

-- Результирующий столбец: user_name (имя из первого SELECT)

ORDER BY с UNION

-- ✅ В конце
SELECT x FROM a
UNION
SELECT x FROM b
ORDER BY x;

-- ❌ Внутри отдельного SELECT — бессмысленно
SELECT x FROM a ORDER BY x UNION SELECT x FROM b;

ORDER BY применяется ко всему результату UNION, поэтому ставится в конце.

Прокачай SQL для собеса
500+ задач по SQL: оконные функции, JOIN, CTE — с разбором каждой
Тренировать SQL в Telegram

LIMIT с UNION

-- LIMIT в одной части
(SELECT x FROM a LIMIT 10)
UNION ALL
(SELECT x FROM b LIMIT 10);

-- LIMIT на весь результат
SELECT x FROM a UNION ALL SELECT x FROM b LIMIT 20;

Скобки помогают явно разделить области действия.

Классические кейсы

1. Разные сущности под одной шапкой

SELECT 'ORDER' AS type, order_id AS id, created_at FROM orders
UNION ALL
SELECT 'return' AS type, return_id AS id, created_at FROM returns
ORDER BY created_at DESC;

Единый лог событий из двух таблиц.

2. Шахматная доска из двух регионов

SELECT 'RU' AS region, user_id, amount FROM orders_ru
UNION ALL
SELECT 'BY' AS region, user_id, amount FROM orders_by;

Затем GROUP BY region — получим статистику по регионам одним запросом.

3. Эмуляция FULL OUTER JOIN

SELECT a.id, a.val, b.val FROM a LEFT JOIN b ON a.id = b.id
UNION ALL
SELECT b.id, a.val, b.val FROM b LEFT JOIN a ON a.id = b.id WHERE a.id IS NULL;

В СУБД без FULL OUTER JOIN (старые MySQL) — классический приём.

К слову, набить руку на таких кейсах удобно через тренажёр в Telegram — разбирайте по 10 вопросов в день, через 2 недели тема становится рефлексом.

Производительность

  • UNION ALL быстрее UNION всегда — нет sort + dedup.
  • Индексы сохраняются: каждый SELECT использует свой индекс.
  • Большой UNION (5+ частей) — часто признак плохого дизайна. Проверьте, нет ли способа объединить в одну таблицу с меткой типа.

10 задач с собеседований

1. Объединить активных и неактивных пользователей

SELECT user_id, 'active' AS status FROM users WHERE last_active >= CURRENT_DATE - INTERVAL '30 day'
UNION ALL
SELECT user_id, 'inactive' FROM users WHERE last_active < CURRENT_DATE - INTERVAL '30 day';

2. Клиенты, которые покупали и в 2025, и в 2026

SELECT user_id FROM orders WHERE EXTRACT(YEAR FROM created_at) = 2025
INTERSECT
SELECT user_id FROM orders WHERE EXTRACT(YEAR FROM created_at) = 2026;

3. Клиенты без заказов

SELECT user_id FROM users
EXCEPT
SELECT user_id FROM orders;

4. Все события в одну ленту

SELECT 'signup' AS event, user_id, registered_at AS ts FROM users
UNION ALL
SELECT 'ORDER', user_id, created_at FROM orders
UNION ALL
SELECT 'payment', user_id, paid_at FROM payments
ORDER BY ts;

5. Распределение по регионам

WITH all_orders AS (
    SELECT 'RU' AS region, amount FROM ru.orders
    UNION ALL
    SELECT 'BY', amount FROM BY.orders
)
SELECT region, SUM(amount) FROM all_orders GROUP BY region;

6. Удалить дубликаты из двух источников

SELECT * FROM source_a
UNION
SELECT * FROM source_b;

UNION сам дедуплицирует — если данные идентичны, получим чистый список.

7. Десктоп + мобайл → один канал «web»

SELECT user_id, amount, 'web' AS channel FROM orders WHERE platform IN ('desktop', 'mobile_web')
UNION ALL
SELECT user_id, amount, 'app' FROM orders WHERE platform = 'app';

8. Тестовая и продакшн-таблицы

SELECT user_id, amount, 'test' AS env FROM test.orders
UNION ALL
SELECT user_id, amount, 'prod' FROM prod.orders;

9. Дополнить данными из истории

SELECT user_id, amount, created_at FROM orders
UNION ALL
SELECT user_id, amount, created_at FROM orders_archive;

10. Сравнить top-5 из двух когорт

SELECT 'cohort_a' AS c, user_id, revenue FROM cohort_a_top5
UNION ALL
SELECT 'cohort_b', user_id, revenue FROM cohort_b_top5
ORDER BY revenue DESC;

Как тренироваться

UNION — простая тема, но на собесе ловят через тонкие вопросы (UNION vs UNION ALL, порядок столбцов, INTERSECT). Знание разницы между UNION и UNION ALL — обязательный минимум.

Тренажёр Карьерник содержит задачи на UNION, INTERSECT, EXCEPT — с разборами типичных ошибок.

Совет: по умолчанию пишите UNION ALL. UNION нужен только когда знаете, что дубликаты есть и их надо убрать. В 90% задач UNION ALL — правильный выбор.

Читайте также

FAQ

UNION или UNION ALL по умолчанию?

UNION ALL быстрее. UNION только если нужна дедупликация. На собесе этот вопрос задают в каждом втором интервью — отвечайте UNION ALL.

Когда использовать INTERSECT?

Когда нужны элементы, присутствующие в обеих выборках (пересечение). Аналог WHERE x IN (SELECT x FROM other), но короче и чище.

Почему UNION с ORDER BY внутри даёт ошибку?

ORDER BY применяется к итоговому UNION-результату, а не к каждому SELECT отдельно. Либо выносите ORDER BY в конец, либо оборачивайте части в скобки с LIMIT.

FULL OUTER JOIN через UNION — быстрее или медленнее?

Обычно медленнее — FULL OUTER JOIN оптимизирован на уровне СУБД. UNION-эмуляция нужна только если СУБД не поддерживает FULL OUTER (старый MySQL).