Раздел 42 · Аналитика данных с нуля

UNION, INTERSECT и EXCEPT

junior~10 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Мира · маркетолог магазина настолок «Кубик»

Две рассылки. Первая для тех, кто любит и стратегии, и пати-игры: им подборку «от стратегии к вечеринке». Вторая для тех, кто брал стратегии в прошлом году, а в этом пропал. Сможешь собрать оба списка?

Каждый вопрос Миры это действие над двумя списками. Есть список «кто покупал стратегии» и список «кто покупал пати-игры». Нужны те, кто есть в обоих. Есть «стратеги 2025 года» и «стратеги 2026 года». Нужны те, кто есть в первом, но не во втором. Можно выкручиваться через JOIN и подзапросы, но в SQL для этого есть прямые операции.

Два списка гостей

Ты собираешь вечеринку и у тебя два списка: друзья с работы и друзья по настолкам. Возможны три вопроса:

  • кого вообще позвать? Все из обоих списков, каждого один раз. Это объединение;
  • кто есть в обоих списках? Это пересечение;
  • кто с работы, но не играет в настолки? Это разность.

В SQL им соответствуют операции над множествами: UNION, INTERSECT и EXCEPT. Они ставятся между двумя полными запросами SELECT:

SELECT ...  -- первый список
INTERSECT
SELECT ...  -- второй список

Правило одно: оба запроса должны возвращать одинаковое число столбцов, и столбцы должны быть совместимы по типу. Склеиваются они по позиции, а не по имени. Имена столбцов в ответе берутся из первого запроса.

Пересечение: и то, и другое

Список покупателей стратегий выглядит так:

SELECT o.customer_id
FROM orders o
JOIN order_items i ON i.order_id = o.id
JOIN products p ON p.id = i.product_id
WHERE o.status <> 'cancelled' AND p.genre = 'strategy';

Таких разных покупателей 987. Покупателей пати-игр 1136. Чтобы найти тех, кто есть в обоих списках, ставим между запросами INTERSECT:

SELECT o.customer_id
FROM orders o
JOIN order_items i ON i.order_id = o.id
JOIN products p ON p.id = i.product_id
WHERE o.status <> 'cancelled' AND p.genre = 'strategy'
INTERSECT
SELECT o.customer_id
FROM orders o
JOIN order_items i ON i.order_id = o.id
JOIN products p ON p.id = i.product_id
WHERE o.status <> 'cancelled' AND p.genre = 'party';

Получается 406 покупателей. Повторов в ответе нет, хотя в каждом списке покупатель встречался столько раз, сколько купил игр жанра: INTERSECT их убирает сам.

урок из курса

Урок из курса «Аналитика данных с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.