Выборка, проекция и переименование

Реляционная алгебра — это «арифметика отношений». Начнём с трёх операций, работающих над одной таблицей: выборка, проекция, переименование.

Реляционная алгебра — формальный язык, в котором операции принимают отношения на вход и возвращают отношение на выход. Это свойство замкнутости позволяет комбинировать операции в сколь угодно сложные выражения.

Зачем нужна реляционная алгебра

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

Ключевое свойство — замкнутость: результат любой операции снова отношение, поэтому к нему можно применить следующую операцию. Из этого вырастают цепочки и деревья операций, в которые компилируется SQL-запрос.

Полный и минимальный набор операций

Прежде чем разбирать операции по одной, полезно увидеть карту. Кодд показал, что любой запрос реляционной алгебры выражается через небольшой базовый набор из шести операций: выборка, проекция, объединение, разность, декартово произведение и переименование. Все остальные операции (пересечение, соединения, деление) — лишь удобные сокращения, выводимые из базовых. Например, пересечение R ∩ S равно R − (R − S); соединение — это произведение плюс выборка. Эта «минимальность» важна теоретически: она задаёт точную меру выразительной силы языка. То, что выразимо в этом наборе, называют реляционно полным, и SQL обязан быть как минимум реляционно полным. Дальше мы пройдём операции от простых унарных к составным, помня, что в основе всего — эта шестёрка.

Выборка σ (selection)

Выборка σусловие(R) оставляет из отношения R только те кортежи, которые удовлетворяют условию. Это «горизонтальный срез» — мы отбираем строки, не трогая столбцы. Условие — это предикат над атрибутами: cena > 1000, city = 'Москва'.

Ключевое свойство выборки: она не меняет схему (набор атрибутов остаётся прежним), а лишь уменьшает число кортежей. На вход — отношение, на выход — отношение той же формы, но «уже». Условие может быть составным: σcena > 1000 AND city = 'Москва' объединяет предикаты логическими связками. Важно, что выборка проверяет условие для каждого кортежа независимо — она не умеет сравнивать строки между собой (для этого нужны соединение или группировка). Это делает её самой «дешёвой» операцией: один проход по данным.

Пример: σcena > 1000(Products) — все товары дороже 1000. В SQL выборке соответствует часть WHERE:

CREATE TABLE products (id INTEGER, name TEXT, cena INTEGER);
INSERT INTO products VALUES (1,'Книга',500),(2,'Кресло',8000),(3,'Кружка',300),(4,'Лампа',1500);

-- σ(cena > 1000)(products)
SELECT * FROM products WHERE cena > 1000;

Вывод: две строки — «Кресло» (8000) и «Лампа» (1500): только товары, прошедшие условие.

Проекция π (projection)

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

В SQL проекции соответствует список столбцов после SELECT. Но внимание: чистый SQL SELECT дубликаты НЕ удаляет (он работает с мультимножеством), поэтому, чтобы получить настоящую проекцию из алгебры, нужен DISTINCT.

CREATE TABLE orders (id INTEGER, city TEXT, summa INTEGER);
INSERT INTO orders VALUES (1,'Москва',1500),(2,'Казань',800),(3,'Москва',2300);

-- π(city)(orders) — в алгебре дубликаты удаляются, поэтому DISTINCT
SELECT DISTINCT city FROM orders;

Вывод: «Москва» и «Казань» — два города, хотя строк было три. Без DISTINCT «Москва» вернулась бы дважды — и это уже не точная проекция из алгебры.

Комбинируем выборку и проекцию

Благодаря замкнутости операции вкладываются друг в друга. «Названия товаров дороже 1000» — это проекция от выборки: πnamecena > 1000(Products)). Сначала отбираем строки, потом оставляем нужный столбец.

CREATE TABLE products (id INTEGER, name TEXT, cena INTEGER);
INSERT INTO products VALUES (1,'Книга',500),(2,'Кресло',8000),(3,'Лампа',1500);

-- π(name)(σ(cena > 1000)(products))
SELECT DISTINCT name FROM products WHERE cena > 1000;

Вывод: «Кресло» и «Лампа». В дереве операций сначала выполняется внутренняя выборка, затем внешняя проекция — порядок важен, и именно его оптимизатор СУБД может переставлять ради скорости.

Зачем нужна именно проекция, а не просто «выбор столбцов»

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

Расширенная проекция

На практике проекция в SQL умеет больше, чем просто отбор существующих столбцов: она может вычислять новые значения. Запрос SELECT name, cena * 1.2 AS cena_s_nds FROM products проецирует на имя и вычисленную колонку. В реляционной алгебре это называют обобщённой (расширенной) проекцией — она допускает выражения над атрибутами, а не только сами атрибуты. Это удобное расширение базовой операции; важно лишь помнить, что вычисление над столбцом (особенно в условии WHERE) может помешать использованию индекса — об этом речь в разделе про оптимизацию.

Переименование ρ (rename)

Переименование ρ меняет имя отношения или его атрибутов, не меняя содержимого. Зачем это нужно? Во-первых, чтобы дать осмысленные имена результату. Во-вторых — и это важнее — чтобы соединять таблицу саму с собой: при самосоединении нам нужны два разных имени для одной и той же таблицы, иначе непонятно, к какому экземпляру относится атрибут. В SQL переименованию соответствуют псевдонимы через AS (для столбцов) и алиасы таблиц.

CREATE TABLE employees (id INTEGER, name TEXT, manager_id INTEGER);
INSERT INTO employees VALUES (1,'Анна',NULL),(2,'Борис',1),(3,'Вера',1);

-- ρ позволяет дать таблице два имени e и m для самосоединения
SELECT e.name AS sotrudnik, m.name AS rukovoditel
FROM employees e JOIN employees m ON e.manager_id = m.id;

Вывод: «Борис — Анна» и «Вера — Анна»: каждый сотрудник со своим руководителем. Без переименования (двух алиасов e и m) такой запрос выразить нельзя.

Дерево операций: как читать сложный запрос

Соберём идею урока воедино. Любое выражение реляционной алгебры — это дерево операций: в листьях стоят отношения, в узлах — операции, и результат каждого узла подаётся на вход вышестоящему. Запрос πnamecena > 1000(Products)) — это дерево из двух узлов: внизу выборка над таблицей, над ней — проекция. Чтение запроса «изнутри наружу» (сначала самые вложенные операции) и есть порядок его выполнения. Этот взгляд бесценен: именно в такое дерево СУБД компилирует ваш SQL, а оптимизатор затем переписывает дерево в эквивалентное, но более дешёвое (например, проталкивает выборку поближе к листьям, чтобы раньше отсеять лишние строки). Так абстрактная алгебра оказывается ровно тем языком, на котором СУБД думает о запросах внутри себя — и понимание её даёт вам рентгеновское зрение для любого SQL.

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

  • Забывают про удаление дубликатов в проекции. π в алгебре удаляет повторы, а SQL SELECT — нет; без DISTINCT вы получаете мультимножество, а не отношение.
  • Путают выборку и проекцию. Выборка отбирает строки (σ, WHERE), проекция — столбцы (π, список после SELECT).
  • Считают порядок операций несущественным. πnamecena>1000(R)) и σcena>1000name(R)) дают разное: во втором случае мы уже выкинули столбец cena и не можем по нему фильтровать.

Итог

  • Реляционная алгебра — набор операций «отношение → отношение»; их замкнутость позволяет строить сложные выражения.
  • Выборка σ оставляет строки по условию (≈ WHERE); проекция π оставляет столбцы и удаляет дубликаты (≈ SELECT DISTINCT).
  • Переименование ρ даёт имена и делает возможным самосоединение (≈ алиасы таблиц).
  • Операции комбинируются, и порядок их применения влияет на результат.
Проверьте себя
1. Какой операции реляционной алгебры соответствует условие WHERE в SQL?
AПроекция π
BВыборка σ
CПереименование ρ
DСоединение ⋈
2. Почему точная проекция π в SQL требует DISTINCT?
ADISTINCT ускоряет запрос
BРезультат алгебры — множество без дубликатов, а SELECT возвращает мультимножество
CБез DISTINCT теряются строки
DDISTINCT сортирует результат
3. Зачем нужна операция переименования ρ?
AЧтобы удалять дубликаты
BЧтобы дать имена и сделать возможным самосоединение таблицы с собой
CЧтобы сортировать строки
DЧтобы фильтровать столбцы
Поддержать проект