Learning Platform
Глоссарий Troubleshooting
Урок 05.01 · 18 мин
Начальный
relational-modelcoddrelationdomain

Codd 1970: relation, tuple, attribute, domain

В предыдущих модулях вы рисовали ER-диаграммы: сущности, атрибуты, связи. Это удобный способ думать о данных, но ER-диаграмма — это не математика. Её нельзя «вычислить», нельзя доказать, что запрос к ней корректен. Реляционная модель — это другое. Это формальная теория, в которой таблица — это математический объект, а запрос — это операция над этим объектом с предсказуемым результатом.

В 1970 году сотрудник IBM Edgar F. Codd опубликовал статью «A Relational Model of Data for Large Shared Data Banks». До неё базы данных были иерархическими (дерево) или сетевыми (граф): чтобы достать запись, программист вручную писал навигацию по указателям. Codd предложил радикальную идею: отделить логическое представление данных от физического хранения. Пользователь описывает, ЧТО он хочет получить, в терминах множеств и отношений; СУБД сама решает, КАК это достать с диска. Эта идея — фундамент SQL и всех реляционных СУБД, которыми мы пользуемся сегодня.

Этот урок — про четыре базовых термина модели Codd. Их легко спутать с «таблица, строка, столбец, тип данных» — и в первом приближении это верно, но различия принципиальны, и без точных определений вы не поймёте, почему дальше работают ключи, нормализация и реляционная алгебра.

Domain: множество допустимых значений

Начнём не с relation, а с domain — потому что domain первичен, всё остальное строится на нём.

Domain — это именованное множество допустимых значений одного типа. Не «тип данных» в смысле языка программирования, а именно множество конкретных значений, которые имеют смысл для предметной области.

Примеры:

  • Domain EmailAddress — множество всех синтаксически корректных email-адресов.
  • Domain AgeYears — множество целых чисел от 0 до 150.
  • Domain CurrencyCode — множество строк из трёх букв: {USD, EUR, RUB, JPY, ...}.
  • Domain OrderStatus — множество {new, paid, shipped, cancelled}.

Domain отвечает на вопрос «какие значения вообще допустимы для этого атрибута». Тип данных СУБД (INTEGER, VARCHAR(3)) — лишь грубое приближение domain. VARCHAR(3) разрешает строку 'XYZ', но в domain CurrencyCode её нет. Поэтому в реальной схеме domain доуточняют через CHECK-constraint или ENUM — об этом будет отдельный урок про constraints.

Ключевое свойство domain: значения из разных domains несравнимы, даже если физически это одинаковый тип. Если age определён над domain AgeYears, а shoe_size — над ShoeSizeEU, то сравнение age = shoe_size в строгой реляционной модели бессмысленно — это разные множества. SQL это правило ослабляет (сравнит два INTEGER без вопросов), и именно поэтому SQL иногда позволяет писать запросы, которые синтаксически валидны, но семантически абсурдны.

Attribute, tuple, relation

Теперь три остальных термина — снизу вверх.

Attribute — это пара «имя + domain». Например, attribute email определён над domain EmailAddress. Attribute — это столбец, но важно: столбец опознаётся по имени, а не по позиции. В реляционной модели нет «третьего столбца», есть только «столбец email».

Tuple — это строка: набор пар «имя атрибута -> значение», по одному значению на каждый attribute relation. Формально tuple — это функция, которая каждому имени атрибута сопоставляет значение из соответствующего domain. Запись tuple:

{ user_id: 42, email: '[email protected]', age: 29 }

Поскольку tuple — это набор именованных пар, порядок пар в нём не имеет значения. Записать тот же tuple как { age: 29, user_id: 42, email: '[email protected]' } — это буквально тот же самый tuple.

Relation — это множество tuples, у которых одинаковый набор атрибутов. Слово «множество» (set) здесь — не метафора, а точный термин из теории множеств, и из него следуют все остальные свойства, которые мы разберём в следующем уроке: в множестве нет дубликатов и нет порядка элементов.

У relation две части:

  • Heading (заголовок) — фиксированный набор атрибутов (имена + domains). Это схема relation, она не меняется от данных.
  • Body (тело) — множество tuples, соответствующих heading. Это и есть данные; они меняются при каждом INSERT/UPDATE/DELETE.
Слои реляционной модели: от domain к relation
DomainИменованное множество допустимых значений: AgeYears = целые 0..150. Первичное понятие, всё строится на нём.
имя + domain
AttributeПара имя+domain: атрибут 'age' над domain AgeYears. Опознаётся по имени, не по позиции.
по значению на атрибут
TupleНабор именованных пар атрибут->значение. Порядок пар не важен — это функция от имени атрибута.
множество tuples
RelationМножество (set) tuples с одинаковым heading. Из 'множества' следуют отсутствие дубликатов и порядка.

Relation против таблицы: в чём подвох

Слово «таблица» в SQL и слово «relation» в теории — почти синонимы, но «почти» здесь важно. Relation — это идеализированный математический объект. Таблица SQL — его инженерная реализация, в которой ради практичности сделаны послабления.

АспектRelation (теория Codd)Таблица (SQL)
Дубликаты строкЗапрещены (это множество)Разрешены, если нет PRIMARY KEY или UNIQUE
Порядок строкНе существуетФизический порядок есть; SELECT без ORDER BY его не гарантирует
Порядок столбцовНе существуетЕсть; SELECT * возвращает столбцы в порядке CREATE TABLE
Безымянные столбцыНевозможныВозможны: SELECT count(*) даёт столбец без имени
NULLВ исходной модели Codd отсутствовалЕсть, и ломает многое (отдельный урок)

Главный практический вывод: таблица без PRIMARY KEY — это не relation, а «мешок» (bag, multiset), в котором могут лежать одинаковые строки. Реляционная теория к такому мешку неприменима: рассуждения о ключах, нормализации, корректности декомпозиции опираются на то, что строки уникальны. Поэтому правило «у каждой таблицы должен быть первичный ключ» — это не стилистический совет, а условие, при котором таблица вообще остаётся реляционным объектом.

-- Это relation: строки гарантированно уникальны
CREATE TABLE users (
    user_id INTEGER PRIMARY KEY,
    email   TEXT NOT NULL,
    age     INTEGER
);

-- Это НЕ relation, а bag: можно вставить две одинаковые строки
CREATE TABLE log_lines (
    message TEXT
);
INSERT INTO log_lines VALUES ('ping'), ('ping');
SELECT count(*) FROM log_lines;
-- count
-- -----
--     2      <- два идентичных tuple; в relation такого быть не может
NOTE

Codd ввёл термины relation/tuple/attribute сознательно, чтобы оторвать мышление инженера от «таблицы как куска памяти». «Таблица» наводит на мысль о ячейках, координатах, позициях. «Relation» — о множестве фактов. Разные слова — разный способ думать о данных.

Почему модель построена на множествах

Зачем Codd взял именно теорию множеств? Потому что у множеств есть готовый, проверенный веками аппарат операций: объединение, пересечение, разность, декартово произведение. Если relation — это множество, то к двум relations можно применять эти операции и получать новый relation. Это и есть реляционная алгебра (следующие уроки), а реляционная алгебра — это математический фундамент SQL.

Свойство, ради которого всё затевалось, называется замкнутость (closure): результат любой операции над relation(s) — это снова relation. Из замкнутости следует, что операции можно вкладывать друг в друга без ограничений: результат одного SELECT подать на вход другому. Подзапросы, CTE, представления (views) — всё это работает именно потому, что модель замкнута. Если бы операция над таблицами иногда возвращала «не таблицу», вложенность была бы невозможна.

Relation и SQL — от математики к синтаксису
Замкнутость: результат операции — снова relation
Relation AМножество tuples — входной реляционный объект.
операция
Relation BРезультат операции — снова relation, его можно подать на вход следующей операции.
снова операция
Relation CВложенность операций без ограничений — основа подзапросов, CTE и представлений.

Попробуй сам

Возьмите любую знакомую сущность — например, «книга в библиотеке». Выпишите на бумаге:

  1. Пять атрибутов книги (isbn, title, pages, published_year, language).
  2. Для каждого атрибута — его domain: точное множество допустимых значений (не тип СУБД, а именно «какие значения имеют смысл»). Для published_year это, например, целые от 1450 до текущего года; для language — конечный список кодов языков.
  3. Один конкретный tuple — строку с реальными значениями, записанную как набор именованных пар.
  4. Ответьте: если вы вставите два tuple с одинаковым isbn — это всё ещё relation? Почему нет, и какой constraint это запрещает?

Затем создайте таблицу books в SQLite или PostgreSQL с PRIMARY KEY и убедитесь, что СУБД отклоняет вставку дубликата по ключу. Это и есть граница между «relation» и «bag» на практике.


Проверка знанийKnowledge check
Почему таблица SQL без объявленного первичного ключа, строго говоря, не является relation в смысле модели Codd, и какое практическое последствие из этого вытекает?
ОтветAnswer
Relation по определению Codd — это множество (set) tuples, а множество не может содержать одинаковых элементов. Таблица SQL без PRIMARY KEY и без UNIQUE-ограничения допускает вставку полностью идентичных строк, то есть является не множеством, а мультимножеством (bag, multiset). Практическое последствие в том, что весь аппарат реляционной теории — рассуждения о candidate keys, о нормальных формах, о корректности (lossless-join) декомпозиции — опирается на уникальность строк. Если строки могут дублироваться, эти рассуждения перестают быть верными: например, нельзя однозначно сослаться на конкретную строку, нельзя гарантировать, что декомпозиция и обратное соединение дадут исходные данные. Поэтому правило "у каждой таблицы должен быть первичный ключ" — это не вопрос стиля, а условие, при котором таблица остаётся полноценным реляционным объектом, к которому применима теория.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Что такое domain в реляционной модели Codd?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5