Множественные операции и произведение

Отношения — это множества, значит, к ним применимы операции теории множеств. Разберём объединение, пересечение, разность и произведение.

Совместимость по объединению — два отношения совместимы, если у них одинаковое число атрибутов и соответствующие атрибуты определены над одинаковыми доменами. Только такие отношения можно объединять, пересекать и вычитать.

Зачем нужны множественные операции

Раз отношение — множество кортежей, естественно спросить: «какие строки есть в обеих таблицах?», «какие есть в первой, но не во второй?». Это вопросы теории множеств, и реляционная алгебра прямо заимствует её операции. На практике они отвечают на запросы вида «клиенты, которые покупали и в 2024, и в 2025», «товары, которые ни разу не заказывали».

Условие совместимости

Объединение, пересечение и разность определены только для совместимых по объединению отношений: одинаковое число столбцов и совпадающие домены по позициям. Нельзя объединить таблицу клиентов с таблицей товаров — это бессмыслица. В SQL это правило выражается в требовании, чтобы у UNION-частей совпадали число и типы столбцов.

Отношения — это множества, отсюда всё

Стоит явно проговорить логику этого урока. В разделе про реляционную модель мы выяснили: тело отношения — это множество кортежей. А над множествами математика давно определила операции: объединение, пересечение, разность. Раз отношение — множество, эти операции применимы к нему «бесплатно», без всякого изобретения. Реляционная алгебра не выдумывает их заново, а наследует из теории множеств, добавив лишь требование совместимости (чтобы операнды были множествами кортежей одной формы). Это прекрасный пример того, как строгое определение окупается: один раз договорившись, что отношение — множество, мы получаем целый пласт операций как следствие. Поэтому если вы помните школьные диаграммы Эйлера-Венна, вы уже наполовину знаете этот урок.

Объединение ∪ (union)

R ∪ S — все кортежи, которые есть в R или в S (или в обоих). Дубликаты, как и положено множеству, удаляются. Запрос «все города, где есть клиенты ИЛИ откуда есть заказы» — это объединение двух проекций.

CREATE TABLE clients_2024 (name TEXT);
CREATE TABLE clients_2025 (name TEXT);
INSERT INTO clients_2024 VALUES ('Анна'),('Борис');
INSERT INTO clients_2025 VALUES ('Борис'),('Вера');

-- R ∪ S: покупали в 2024 ИЛИ в 2025 (без дубликатов)
SELECT name FROM clients_2024
UNION
SELECT name FROM clients_2025;

Вывод: «Анна», «Борис», «Вера» — три имени; «Борис» не задвоился, потому что UNION (в отличие от UNION ALL) удаляет дубликаты, как и алгебраическое объединение.

Порядок столбцов в объединении важен

Есть тонкость, на которой спотыкаются: совместимость в SQL проверяется по позициям, а не по именам столбцов. Если первый запрос отдаёт (name, city), а второй — (city, name), объединение формально пройдёт (типы строковые совпали), но смешает имена с городами — данные окажутся в неправильных колонках. СУБД не спасёт, потому что с её точки зрения типы совпадают. Поэтому при UNION всегда следите, чтобы столбцы в обеих частях шли в одном смысловом порядке. Это практическое следствие теоретического требования совместимости: «одинаковые домены по позициям» — и слово «по позициям» здесь критично.

Пересечение ∩ (intersection)

R ∩ S — кортежи, которые есть и в R, и в S одновременно. «Клиенты, покупавшие в оба года» — это пересечение.

CREATE TABLE clients_2024 (name TEXT);
CREATE TABLE clients_2025 (name TEXT);
INSERT INTO clients_2024 VALUES ('Анна'),('Борис');
INSERT INTO clients_2025 VALUES ('Борис'),('Вера');

-- R ∩ S: покупали в ОБА года
SELECT name FROM clients_2024
INTERSECT
SELECT name FROM clients_2025;

Вывод: «Борис» — единственный, кто есть в обоих списках.

Разность − (difference)

R − S — кортежи, которые есть в R, но отсутствуют в S. Внимание: операция несимметрична, R − S ≠ S − R. «Клиенты 2024 года, которые НЕ вернулись в 2025» — это разность.

CREATE TABLE clients_2024 (name TEXT);
CREATE TABLE clients_2025 (name TEXT);
INSERT INTO clients_2024 VALUES ('Анна'),('Борис');
INSERT INTO clients_2025 VALUES ('Борис'),('Вера');

-- R − S: были в 2024, но не в 2025
SELECT name FROM clients_2024
EXCEPT
SELECT name FROM clients_2025;

Вывод: «Анна» — она покупала в 2024, но не в 2025. Разность лежит в основе запросов «найти то, чего нет»: товары без заказов, клиенты без покупок.

Почему важна совместимость: интуиция

Требование совместимости легко принять как формальность, но за ним стоит здравый смысл. Объединение отвечает на вопрос «какие кортежи есть тут или там» — а чтобы спрашивать про «такие же» кортежи, у них должна быть одинаковая структура. Сложить множество клиентов с множеством товаров бессмысленно ровно потому, что клиент и товар — разные по структуре кортежи, их нельзя считать «одинаковыми или разными». Поэтому ∪, ∩ и − требуют, чтобы операнды были отношениями одной формы. А вот произведение и соединение, наоборот, специально работают с отношениями разной структуры — они их комбинируют, а не сравнивают. Это деление операций на «сравнивающие однотипное» и «комбинирующие разнотипное» — удобный способ их запомнить.

Декартово произведение × (cartesian product)

R × S — каждый кортеж R соединяется с каждым кортежем S. Если в R было m строк, а в S — n, в результате будет m × n строк, а атрибуты — объединение атрибутов обоих отношений. Само по себе произведение почти всегда даёт бессмысленные комбинации (каждый клиент с каждым товаром), поэтому в чистом виде применяется редко.

Но это фундамент: соединение (join) — это произведение с последующей выборкой. Именно через × строго определяются все виды соединений, к которым мы перейдём в следующем уроке. В SQL произведению соответствует CROSS JOIN или перечисление таблиц через запятую без условия.

Здесь кроется и предупреждение о производительности. Поскольку произведение даёт m × n строк, случайное декартово произведение на больших таблицах катастрофично: две таблицы по миллиону строк дадут триллион комбинаций — запрос «зависнет». А ведь именно это происходит, если в соединении забыть условие ON или ошибиться в нём. Поэтому, увидев необъяснимо медленный или «разбухший» результат, первым делом проверяют, не превратилось ли соединение в произведение из-за пропущенного условия. Понимание, что join начинается с произведения, помогает мгновенно распознать эту ошибку.

CREATE TABLE sizes (s TEXT);
CREATE TABLE colors (c TEXT);
INSERT INTO sizes VALUES ('S'),('M');
INSERT INTO colors VALUES ('красный'),('синий');

-- R × S: все комбинации размер×цвет (2×2 = 4 строки)
SELECT s, c FROM sizes CROSS JOIN colors;

Вывод: четыре строки: (S, красный), (S, синий), (M, красный), (M, синий) — все возможные сочетания. Здесь произведение осмысленно: мы строим полный каталог вариантов товара.

Множественные операции и проектирование

Эти операции — не только инструмент запросов, но и способ думать о данных. Разность лежит в основе целого класса практических задач «найти пропущенное»: товары без продаж (все товары − проданные товары), студенты без оценок, клиенты без заказов. Пересечение отвечает на «и то, и другое» (активные И премиум-клиенты). Объединение — на «свести вместе из разных источников» (заказы из онлайна и из офлайна в один отчёт). Привыкнув видеть запрос как операцию над множествами, вы быстрее переводите расплывчатое требование заказчика («покажи, кто покупал в прошлом году, но не в этом») в точный запрос (разность двух множеств клиентов). Это и есть практическая ценность алгебраического взгляда: он даёт словарь, на котором требования формулируются однозначно.

Сводка

ОперацияОбозначениеSQLСовместимость
ОбъединениеR ∪ SUNIONтребуется
ПересечениеR ∩ SINTERSECTтребуется
РазностьR − SEXCEPTтребуется
ПроизведениеR × SCROSS JOINне требуется

Типичные ошибки

  • Объединение несовместимых отношений. UNION требует одинакового числа и типов столбцов; иначе — ошибка или бессмыслица.
  • Путают UNION и UNION ALL. Алгебраическое объединение удаляет дубликаты (как UNION); UNION ALL их сохраняет и алгебре не соответствует.
  • Считают разность симметричной. R − S и S − R дают разное; всегда уточняйте направление.
  • Случайное декартово произведение. Забыли условие соединения — получили m × n строк вместо осмысленного join; классическая причина «взорвавшегося» запроса.

Итог

  • Объединение, пересечение и разность работают только над совместимыми по объединению отношениями.
  • ∪, ∩ удаляют дубликаты; разность несимметрична и отвечает за запросы «найти отсутствующее».
  • Декартово произведение комбинирует каждую строку с каждой и служит фундаментом для соединений.
  • В SQL им соответствуют UNION, INTERSECT, EXCEPT и CROSS JOIN.
Проверьте себя
1. Какое условие нужно для применения объединения, пересечения и разности?
AОдинаковое число строк
BСовместимость по объединению: одинаковое число атрибутов и совпадающие домены
CНаличие первичного ключа
DОдинаковые имена таблиц
2. Чем алгебраическое объединение отличается от SQL UNION ALL?
AНичем
BОбъединение удаляет дубликаты, а UNION ALL их сохраняет
CUNION ALL быстрее работает с индексами
DОбъединение сохраняет дубликаты
3. Сколько строк даст декартово произведение таблиц на 3 и 4 строки?
A7
B12
C3
D4
Поддержать проект