EXCEPT (MINUS): разность множеств

Что такое EXCEPT и зачем он нужен?

EXCEPT (или MINUS в некоторых СУБД) — это оператор для работы с множествами, который возвращает только те строки из первого запроса, которых нет во втором. Это как вычитание в математике, но для таблиц!

-- Простейший пример (PostgreSQL, SQL Server)
SELECT * FROM table1
EXCEPT
SELECT * FROM table2;

-- Аналог в Oracle, MySQL
SELECT * FROM table1
MINUS
SELECT * FROM table2;

📌 Где это применяется?

  • Поиск уникальных данных: например, какие товары есть в одном магазине, но отсутствуют в другом
  • Анализ изменений: какие клиенты были активны в прошлом месяце, но не совершили покупок в текущем
  • Очистка данных: исключение тестовых записей из основного отчёта

Как работает EXCEPT: разбираем на примерах

Пример 1: Сравнение двух списков клиентов

Допустим, у нас есть:

  • old_customers — клиенты за прошлый год
  • new_customers — клиенты за текущий год

Найдём тех, кто перестал пользоваться нашим сервисом:

SELECT customer_id, email FROM old_customers
EXCEPT
SELECT customer_id, email FROM new_customers;

💡 Важно!

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

Пример 2: EXCEPT с условиями

Добавим фильтрацию — найдём только московских клиентов, которые ушли:

SELECT customer_id, email 
FROM old_customers 
WHERE city = 'Москва'
EXCEPT
SELECT customer_id, email 
FROM new_customers 
WHERE city = 'Москва';

Нюансы, о которых важно знать

1. Обработка дубликатов

EXCEPT автоматически удаляет дубликаты в результате (как DISTINCT). Если нужно сохранить повторы — используйте UNION ALL с хитрыми условиями.

2. Сравнение NULL-значений

В SQL NULL ≠ NULL! Поэтому строки с NULL попадут в результат, даже если кажутся одинаковыми:

-- Пример с NULL (вернёт строку)
SELECT 1, NULL
EXCEPT
SELECT 1, NULL;

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

Для больших таблиц EXCEPT может работать медленно. В таких случаях иногда эффективнее использовать NOT EXISTS:

-- Альтернатива EXCEPT
SELECT a.* 
FROM table1 a
WHERE NOT EXISTS (
    SELECT 1 FROM table2 b 
    WHERE a.id = b.id
);

Практика: решаем реальные задачи

Задача 1: Поиск «выпавших» товаров

Есть таблицы products_2023 и products_2024. Найдём товары, которые сняли с продажи:

SELECT product_id, name 
FROM products_2023
EXCEPT
SELECT product_id, name 
FROM products_2024;

Задача 2: Анализ активности пользователей

Какие пользователи зарегистрировались, но ни разу не зашли в систему?

SELECT user_id FROM registrations
EXCEPT
SELECT user_id FROM logins;

EXCEPT vs NOT IN vs NOT EXISTS

Когда что использовать? Краткий гид:

Оператор Плюсы Минусы
EXCEPT Читаемость, простота Медленнее на больших данных
NOT IN Простота синтаксиса Проблемы с NULL
NOT EXISTS Оптимизация, гибкость условий Сложнее писать

Пример для сравнения:

-- NOT IN (осторожно с NULL!)
SELECT id FROM table1 
WHERE id NOT IN (SELECT id FROM table2);

-- NOT EXISTS (без проблем с NULL)
SELECT id FROM table1 a
WHERE NOT EXISTS (SELECT 1 FROM table2 b WHERE a.id = b.id);

Продвинутые приёмы

Комбинирование с другими операторами

EXCEPT можно сочетать с UNION и INTERSECT для сложной аналитики:

-- Найдём товары, которые были в 2023, но исчезли или изменили цену в 2024
(SELECT product_id FROM products_2023
 EXCEPT
 SELECT product_id FROM products_2024)
UNION
(SELECT product_id FROM products_2023
 INTERSECT
 SELECT product_id FROM products_2024
 WHERE price <> (SELECT price FROM products_2023 p WHERE p.product_id = products_2024.product_id));

EXCEPT в CTE

Отлично работает с общими табличными выражениями:

WITH inactive_users AS (
    SELECT user_id FROM all_users
    EXCEPT
    SELECT user_id FROM active_users
)
SELECT * FROM inactive_users
JOIN user_profiles USING (user_id);

Итоги: когда EXCEPT — идеальный выбор

✅ Нужно сравнить два набора данных "в лоб"
✅ Важна читаемость кода (EXCEPT понятнее NOT EXISTS для новичков)
✅ Работаем с небольшими/средними таблицами
✅ Нужно исключить дубликаты в результате

Для углублённого изучения операций с множествами в SQL загляните на YouTube-канал Данилы Бежина — там есть разборы реальных кейсов!

Скрыть рекламу навсегда

🎥 YouTube: программирование простым языком

Канал, где я спокойно и по шагам объясняю сложные темы — без заумных терминов и лишней теории.

Подходит, если раньше «не заходило», но хочется наконец понять.

▶️ Смотреть курсы на YouTube