SQL Studio / Разборы SQL / UNION или UNION ALL: почему изменилось число строк
Разница: UNION соединяет результаты запросов и убирает полностью одинаковые строки. UNION ALL сохраняет все строки, включая повторы. Ни один вариант не «правильнее» без понимания, что означает строка.
1. UNION: три уникальных клиента вместо четырёх записей
В первом списке клиенты 1 и 2, во втором — 2 и 3. Эти данные создаются через VALUES, таблицы заранее не нужны.
WITH a(customer_id) AS (VALUES (1), (2)),
b(customer_id) AS (VALUES (2), (3))
SELECT customer_id FROM a
UNION
SELECT customer_id FROM b
ORDER BY customer_id;
Результат — 1, 2, 3. Клиент 2 встретился дважды, но в итоговом списке остался один раз. Это подходит, если требуется множество уникальных идентификаторов.
2. UNION ALL: сохраняем оба появления клиента
WITH a(customer_id) AS (VALUES (1), (2)),
b(customer_id) AS (VALUES (2), (3))
SELECT customer_id FROM a
UNION ALL
SELECT customer_id FROM b
ORDER BY customer_id;
Результат — 1, 2, 2, 3. Если строки отражают события, второй клиент мог совершить два действия. Убирать одну запись только из-за одинакового customer_id было бы ошибкой.
3. Повторный платёж — не дубликат строки
В учебной базе payments заказ может иметь неудачную и успешную попытки. Объединим номера заказов из обеих групп и проверим, какие встретились дважды.
SELECT order_id, COUNT(*) AS attempts_n
FROM (
SELECT order_id FROM payments WHERE status = 'failed'
UNION ALL
SELECT order_id FROM payments WHERE status = 'captured'
)
GROUP BY order_id
HAVING COUNT(*) > 1
ORDER BY order_id;
| order_id | attempts_n |
|---|---|
| 201 | 2 |
| 206 | 2 |
Если заменить UNION ALL на UNION, повтор номера заказа исчезнет, и запрос ошибочно покажет, что таких заказов нет. При этом сами попытки оплаты никуда из базы не пропали — потеря произошла только в результате SQL.
4. UNION ALL для денежных движений
Поступления и возвраты имеют разные знаки. Их удобно объединить в поток движений, затем посчитать итог. Берём только подтверждённые платежи и все возвраты из учебной таблицы.
WITH movements AS (
SELECT amount_kopecks AS delta
FROM payments WHERE status = 'captured'
UNION ALL
SELECT -amount_kopecks AS delta
FROM refunds
)
SELECT SUM(delta) / 100.0 AS net_rub
FROM movements;
Получится 1 160 ₽: поступления 1 250 ₽ минус возвраты 90 ₽. Здесь UNION опасен: если два движения имеют одинаковые суммы, он может удалить одно из них. В финансовых данных у каждого события должен сохраняться собственный идентификатор.
Проверить самостоятельно
Сначала посмотрите на попытки оплат, затем проверьте денежный итог без соединения нескольких таблиц «один ко многим».
Синтаксис объединения результатов: официальная документация SQLite.
Для последовательной практики на MySQL 8 — курс «SQL для аналитика» на Stepik с итоговой работой и возможностью получить сертификат.