SQLLab
Все статьи

UNION и UNION ALL в SQL: объединение результатов запросов

Как работают UNION и UNION ALL в SQL: разница, примеры, когда использовать каждый, комбинация с ORDER BY и производительность.

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

UNION объединяет результаты двух и более SELECT-запросов в один набор строк. Это как сложить два списка — но с условиями совместимости типов данных.

Синтаксис

SELECT column1, column2 FROM table1
UNION
SELECT column1, column2 FROM table2;

Правила:

  • Количество столбцов в обоих запросах должно совпадать
  • Типы данных должны быть совместимы (или автоматически приводимы)
  • Имена столбцов в результате берутся из первого запроса

UNION vs UNION ALL: главное отличие

-- UNION: убирает дубликаты (дороже)
SELECT city FROM customers
UNION
SELECT city FROM suppliers;

-- UNION ALL: оставляет все строки (быстрее)
SELECT city FROM customers
UNION ALL
SELECT city FROM suppliers;

Пример:

customers: Москва, Питер, Москва suppliers: Москва, Екатеринбург

UNION (без дублей)UNION ALL (с дублями)
МоскваМосква
ПитерПитер
ЕкатеринбургМосква
Москва
Екатеринбург

UNION ALL всегда быстрее — он не делает сортировку/хеширование для поиска дублей. Если дубликаты не важны или их не будет — всегда используйте UNION ALL.


Практические примеры

Объединение двух таблиц одной структуры

-- Все транзакции: пополнения и списания в одном списке
SELECT
    created_at,
    amount,
    'deposit' AS type
FROM deposits
WHERE user_id = 42

UNION ALL

SELECT
    created_at,
    amount,
    'withdrawal' AS type
FROM withdrawals
WHERE user_id = 42

ORDER BY created_at DESC;

«Стек» запросов для разных условий

-- VIP-клиенты: либо потратили > 100к, либо зарегистрированы > 2 лет назад
SELECT user_id, 'big_spender' AS reason
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 100000

UNION

SELECT user_id, 'loyal_customer' AS reason
FROM users
WHERE created_at < NOW() - INTERVAL '2 years';

Добавить «синтетическую» строку

-- Список с итоговой строкой
SELECT department, SUM(salary) AS total
FROM employees
GROUP BY department

UNION ALL

SELECT 'ИТОГО', SUM(salary)
FROM employees

ORDER BY department;

ORDER BY с UNION

ORDER BY применяется ко всему результату и пишется в конце:

SELECT name, 'employee' AS source FROM employees
UNION ALL
SELECT name, 'contractor' AS source FROM contractors
ORDER BY name ASC;  -- ← сортирует весь объединённый результат

Нельзя сортировать каждую часть отдельно (только через подзапрос):

-- Ошибка:
SELECT name FROM employees ORDER BY name
UNION ALL
SELECT name FROM contractors;

-- Правильно (если нужен разный порядок):
SELECT * FROM (SELECT name, 1 AS priority FROM employees ORDER BY name LIMIT 10) e
UNION ALL
SELECT * FROM (SELECT name, 2 AS priority FROM contractors ORDER BY name LIMIT 10) c;

INTERSECT и EXCEPT — родственники UNION

В стандарте SQL есть ещё два оператора работы с множествами:

-- INTERSECT: только строки, которые есть в ОБОИХ результатах
SELECT user_id FROM buyers
INTERSECT
SELECT user_id FROM newsletter_subscribers;

-- EXCEPT: строки из первого, которых нет во втором
SELECT user_id FROM all_users
EXCEPT
SELECT user_id FROM active_users;
-- = пользователи, которые никогда не были активны

PostgreSQL поддерживает все три оператора. Аналоги INTERSECT ALL и EXCEPT ALL (с дублями) тоже доступны.


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

-- Медленно: UNION делает дедупликацию
SELECT email FROM customers UNION SELECT email FROM leads;

-- Быстрее: UNION ALL если дублей нет или они не важны
SELECT email FROM customers UNION ALL SELECT email FROM leads;

Для больших таблиц разница может быть кратной. Проверяйте план через EXPLAIN ANALYZE:

EXPLAIN ANALYZE
SELECT email FROM customers UNION SELECT email FROM leads;

В плане UNION выглядит как HashAggregate или Sort + Unique, UNION ALL — просто Append.


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

Разное число столбцов

-- Ошибка: в первом 3 столбца, во втором 2
SELECT id, name, email FROM users
UNION
SELECT id, email FROM admins;

Несовместимые типы

-- Ошибка: нельзя объединить int и varchar без явного приведения
SELECT id FROM users
UNION
SELECT name FROM users;

-- Правильно:
SELECT id::text FROM users
UNION
SELECT name FROM users;

Потеря имён столбцов

Имена берутся из первого запроса — это важно при ORDER BY:

SELECT order_id AS id, total FROM orders
UNION ALL
SELECT transaction_id, amount FROM refunds
ORDER BY id;  -- "id" — это имя из первого запроса (order_id)

Итог

UNIONUNION ALL
Убирает дубликатыДаНет
СкоростьМедленнееБыстрее
Когда использоватьКогда дубликаты недопустимыВсегда когда возможно

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

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

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

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

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