Множественные операции и произведение
Отношения — это множества, значит, к ним применимы операции теории множеств. Разберём объединение, пересечение, разность и произведение.
Совместимость по объединению — два отношения совместимы, если у них одинаковое число атрибутов и соответствующие атрибуты определены над одинаковыми доменами. Только такие отношения можно объединять, пересекать и вычитать.
Зачем нужны множественные операции
Раз отношение — множество кортежей, естественно спросить: «какие строки есть в обеих таблицах?», «какие есть в первой, но не во второй?». Это вопросы теории множеств, и реляционная алгебра прямо заимствует её операции. На практике они отвечают на запросы вида «клиенты, которые покупали и в 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 ∪ S | UNION | требуется |
| Пересечение | R ∩ S | INTERSECT | требуется |
| Разность | R − S | EXCEPT | требуется |
| Произведение | R × S | CROSS JOIN | не требуется |
Типичные ошибки
- Объединение несовместимых отношений.
UNIONтребует одинакового числа и типов столбцов; иначе — ошибка или бессмыслица. - Путают
UNIONиUNION ALL. Алгебраическое объединение удаляет дубликаты (какUNION);UNION ALLих сохраняет и алгебре не соответствует. - Считают разность симметричной. R − S и S − R дают разное; всегда уточняйте направление.
- Случайное декартово произведение. Забыли условие соединения — получили m × n строк вместо осмысленного join; классическая причина «взорвавшегося» запроса.
Итог
- Объединение, пересечение и разность работают только над совместимыми по объединению отношениями.
- ∪, ∩ удаляют дубликаты; разность несимметрична и отвечает за запросы «найти отсутствующее».
- Декартово произведение комбинирует каждую строку с каждой и служит фундаментом для соединений.
- В SQL им соответствуют
UNION,INTERSECT,EXCEPTиCROSS JOIN.