Перейти к основному содержимому

Двенадцать правил Кодда

Разработчику Аналитику Архитектору

Где применяют новичку двенадцать правил

В теоретических основах мы уже встретили Эдгара Кодда — автора реляционной модели (1970), и идею реляционной полноты. В 1985–1986 годах он дополнил теорию двенадцатью правилами: это практический чек-лист, по которому можно понять, строится ли продукт на реляционной модели по сути, или слово «реляционный» в брошюре — лишь маркетинг.

Важно сразу: PostgreSQL, MySQL, Oracle редко совпадают с правилами буквально на 12 из 12. Правила — компас при выборе СУБД и проектировании API. Они отвечают на вопросы вроде:

  • почему данные лежат в таблицах, а метаданные — в каталоге, который тоже можно запросить SQL;
  • зачем нужен NULL и почему NULL = NULL ведёт себя странно;
  • почему приложение пишет SELECT … FROM users, а DBA может перенести таблицу на другой диск без правки кода;
  • почему нельзя «починить» базу, копируя папку с файлами PostgreSQL в проводнике.

Если вы только начинаете путь в SQL — прочитайте сначала Знакомство с базами данных и Реляционную модель. Здесь мы связываем теорию с тем, что вы увидите в information_schema, VIEW и миграциях.

Мини-глоссарий перед правилами

ТерминПростыми словами
Отношение (relation)Таблица: строки — кортежи, столбцы — атрибуты с типом.
Каталог (catalog)«Паспорт» БД: список таблиц, столбцов, ключей, прав — хранится внутри СУБД и читается запросами.
Представление (VIEW)Сохранённый запрос с именем; для приложения выглядит как таблица.
Реляционная полнотаЯзык (SQL) умеет выразить все основные операции над таблицами: выбор, соединение, проекция, объединение и т.д.
НезависимостьПриложение опирается на имена таблиц и столбцов; способ хранения на диске может меняться «под капотом».
ЦелостностьПравила «так нельзя»: дубликат ключа, заказ без клиента, отрицательный остаток — СУБД отклонит операцию.

Нулевое правило — фундамент

Формулировка Кодда (смысл): всё, что СУБД делает с сохранёнными данными, должно выполняться реляционными средствами — таблицами, операциями над отношениями, каталогом.

Если совсем просто: база — это не «папка с Excel и куча скриптов вокруг». Единственный официальный способ прочитать и изменить данные — язык запросов (SQL) и механизмы СУБД (транзакции, ограничения). Файлы на диске (base/, .mdf) — внутренняя кухня; пользователь и приложение с ними не работают напрямую.

На практике:

ДействиеСоответствует правилу 0?
INSERT INTO orders … через JDBC / ORM
Отчёт в BI по SELECT к PostgreSQL
Скрипт правит байты в каталоге base/ на диске✗ (см. правило 12)
«Главные» заказы в JSON на диске, в SQL только справочник✗ (другая модель хранения)

Двенадцать правил — обзорная таблица

Нумерация и формулировки близки к классическому изложению Кодда (иногда правило 0 не считают, тогда остаётся ровно двенадцать «нумерованных» требований к продукту).

Название (кратко)Что требует Кодд
1ИнформационноеВся информация в БД представлена только как значения в таблицах (отношениях).
2Гарантированный доступЛюбое значение логически доступно по имени таблицы + первичный ключ + имя столбца.
3Обработка NULLNULL поддерживается единообразно во всех операциях и типах, где это уместно.
4Активный каталогОписание БД (метаданные) хранится в реляционном каталоге и доступно через язык запросов, как обычные данные.
5Реляционно полный языкЕсть язык (SQL), на котором можно выразить все операции реляционной алгебры (реляционная полнота).
6Обновляемые представленияВсе представления, которые теоретически можно обновлять, система должно уметь обновлять.
7Высокоуровневые операцииINSERT / UPDATE / DELETE применимы к целому отношению (набору строк), а не только к одной строке за раз на уровне языка.
8Физическая независимостьИзменение способа хранения на диске не ломает приложения (индексы, партиции, перенос файлов).
9Логическая независимостьИзменение логической схемы (новые столбцы, таблицы) не ломает приложения, если контракт сохранён (часто через VIEW).
10Независимость целостностиОграничения целостности задаются в каталоге и могут меняться без перекомпиляции приложений.
11Независимость распределенияПри распределённой БД приложение не «знает», на каком узле лежат данные.
12НеподрывНизкоуровневый доступ не должен позволять обойти ограничения целостности и правила СУБД.

Дальше — по каждому правилу с пояснением и комментарием «как в жизни».


Правило 1. Информационное

Идея: в реляционной БД нет «специальных» мест для метаданных, настроек и бизнес-фактов на разных принципах — всё сводится к отношениям.

Пример нарушения духа правила: хранить заказы в MongoDB, а «справочник статусов» только в конфиге приложения — это уже другая модель (не единое реляционное хранилище).

В SQL: таблицы orders, users, плюс каталог information_schema — всё в одной парадигме.


Правило 2. Гарантированный доступ

Идея: чтобы получить, например, email пользователя с id = 42, достаточно знать три вещи:

  1. имя таблицы (отношения) — users;
  2. значение первичного ключа42;
  3. имя столбцаemail.

Номер страницы на диске, смещение в файле, «третья строка снизу в файле» — не входят в контракт.

Разбор запроса по частям:

SELECT email -- (3) какой атрибут нужен
FROM users -- (1) из какого отношения
WHERE id = 42; -- (2) по какому ключу ищем строку
  • SELECT — «верни столбец»;
  • FROM — «из таблицы»;
  • WHERE id = 42 — «строка, у которой первичный ключ равен 42».

Оптимизатор может пойти через индекс по id или прочитать всю таблицу — смысл для программиста тот же (правило 8: физика скрыта).

Составной ключ: в таблице order_items ключ (order_id, line_no). Тогда доступ — по паре:

SELECT product_id
FROM order_items
WHERE order_id = 1001 AND line_no = 3;

Одного order_id недостаточно: в заказе много строк.


Правило 3. Систематическая обработка NULL

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

NULL — это не ноль и не пустая строка ''. Это специальная метка «значение отсутствует или неизвестно».

Разбор выражений (трёхзначная логика):

ВыражениеРезультатПочему
NULL = NULLне TRUEСравнить «неизвестное» с «неизвестным» нельзя утвердить
5 = NULLUNKNOWNТо же
email IS NULLTRUE или FALSEЕдинственный правильный способ проверить «пусто»
COUNT(*) по таблицесчитает все строкиNULL в ячейке строку не убирает
COUNT(email)без NULL в emailПустые email не входят в счётчик
-- Ошибка новичка:
SELECT * FROM users WHERE middle_name = NULL; -- всегда пустой результат

-- Правильно:
SELECT * FROM users WHERE middle_name IS NULL;

Подробнее: Реляционная модель.

На проектировании: иногда вместо «магического» NULL вводят отдельную таблицу или флаг has_middle_name — это инженерный выбор, а не отмена правила 3.


Правило 4. Активный каталог

Идея: описание базы (какие есть таблицы, столбцы, типы, ключи) хранится в самой базе в виде обычных таблиц — активный каталог. Его можно прочитать тем же SQL, что и заказы с клиентами.

Пример — «паспорт» таблицы users:

SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'users';

Каждая строка результата — один столбец: имя, тип (integer, text), можно ли NULL.

Зачем это вам:

  • IDE и DBeaver строят дерево таблиц из каталога;
  • Flyway / Liquibase сравнивают желаемую схему с фактической;
  • ORM (Hibernate, SQLAlchemy) при старте может сверить модель с БД.

В PostgreSQL: information_schema (стандарт SQL), pg_catalog (внутренние таблицы PG) — Словарь данных и каталоги.


Правило 5. Реляционно полный язык

Идея: один язык (SQL) выражает выборку, соединение, проекцию, объединение, разность — всё, что даёт реляционная алгебра.

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


Правило 6. Обновляемые представления

Идея: VIEW — сохранённый запрос с именем. Кодд требует: если представление логически можно обновить (однозначно понятно, какая строка базовой таблицы меняется), СУБД должна это уметь.

Простое обновляемое представление (одна таблица):

CREATE VIEW active_users AS
SELECT id, email FROM users WHERE deleted_at IS NULL;

UPDATE active_users SET email = 'new@example.com' WHERE id = 42;
-- PostgreSQL перенаправит UPDATE в таблицу users

Скорее всего read-only (движок не знает, какую строку orders трогать):

CREATE VIEW revenue_by_customer AS
SELECT customer_id, SUM(amount) AS total
FROM orders
GROUP BY customer_id;
-- UPDATE revenue_by_customer SET total = 0 -- бессмысленно для СУБД

Признаки «сложного» view: JOIN нескольких таблиц, GROUP BY, DISTINCT, выражения в SELECT без однозначной обратной проекции. В PostgreSQL смотрите information_schema.views и документацию по updatable views; альтернатива — INSTEAD OF триггер или обновление через API к базовым таблицам.


Правило 7. Высокоуровневые вставка, изменение, удаление

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

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
DELETE FROM sessions WHERE expires_at < NOW();

СУБД сама найдёт затронутые строки, проверит ограничения, запишет WAL. Это связано с транзакциями: одна команда может затронуть тысячи строк атомарно.


Правило 8. Физическая независимость

Идея: меняется как данные лежат на диске — не меняется что видит приложение в SQL.

Меняет DBA «под капотом»Меняет ли код приложения?
Таблица переехала на SSDНет
Добавили индекс CREATE INDEX …Нет (тот же SELECT)
Партиционирование по месяцамНет
Сменили shared_buffers в конфигеНет

Приложение по-прежнему шлёт SELECT email FROM users WHERE id = 42. Оптимизатор сам решит: идти по индексу или сканировать таблицу.

Связь с энциклопедией: страницы, буферный пул, физическое хранение.

Когда независимость ослабляют осознанно: хинты плана (/*+ IndexScan */), жёсткая привязка к партиции — редкий инженерный компромисс.


Правило 9. Логическая независимость

Идея: меняется структура таблиц (новые столбцы, разбиение сущности) — старые программы продолжают работать через стабильный интерфейс: VIEW, API, промежуточный слой.

УровеньПравило 8 (физика)Правило 9 (логика)
Что меняетсяДиск, индексы, партицииИмена таблиц/столбцов, нормализация
Кто чаще меняетDBAАрхитектор, разработчик (миграции)
Типичный приёмCREATE INDEXCREATE VIEW users_legacy AS …

Пример: таблицу users разделили на users + user_profiles, но VIEW users_legacy отдаёт прежний набор столбцов — см. роль БД в организации (Expand–Contract).

Связь с проектированием: Проектирование баз данных.


Правило 10. Независимость целостности

Идея: PRIMARY KEY, FOREIGN KEY, CHECK, UNIQUE хранятся в каталоге, а не размазаны по коду на Python/Java.

Выгода: одно место правды — СУБД отклонит «битую» вставку даже из консоли DBA или скрипта миграции. Синтаксис: Ограничения в SQL.


Правило 11. Независимость распределения

Идея: при шардировании или репликах клиент обращается к логической БД; маршрутизация на узлы — задача middleware или СУБД.

Современность: PostgreSQL с Citus, Oracle RAC, распределённый SQL (CockroachDB, Spanner) — разные степени выполнения. Для одного сервера правило выполняется тривиально.


Правило 12. Неподрыв (nonsubversion)

Идея: если есть низкоуровневый API (файлы данных, процедуры обхода движка), он не должен позволять нарушить ограничения, которые SQL соблюдает.

Почему это важно: именно поэтому «починим БД, скопировав .mdf руками» — путь с риском потери целостности. Администрирование через поддерживаемые утилиты (pg_dump, RMAN, BACKUP DATABASE) сохраняет контракт СУБД.


SQL и «чистая» модель — где учебник расходится с жизнью

Кодд описывал отношения как множества — без дубликатов строк. SQL с самого начала работал с мультимножествами (bag): SELECT без DISTINCT может вернуть одинаковые строки несколько раз. Это не «ломает» реляционность полностью, но объясняет, почему в production принято:

  • явно проектировать первичные ключи;
  • использовать DISTINCT, когда нужна уникальность результата;
  • не полагаться на «случайный» порядок строк без ORDER BY.

NULL — второй вечный спор. Правило 3 требует системной семантики; в SQL она есть (трёхзначная логика), но NULL перегружен: «неизвестно», «не применимо», «ещё не заполнили». На проектировании иногда заменяют nullable поля отдельными таблицами или явными флагами — это уже инженерный компромисс, не отмена правила.

ТемаИдеал КоддаПрактика SQL
Дубликаты строкНетДопустимы без DISTINCT
Порядок строкНе определёнORDER BY обязателен для стабильного UI
NULLЕдиная семантикаМощно, но требует дисциплины
Вложенные таблицыНе в 1НФJSON/массивы в PostgreSQL — осознанное расширение

Правила Кодда помогают задавать вопросы при выборе СУБД и проектировании API, а не требуют буквального «SQL = чистая математика».


Разбор на примере — правила 2, 6 и 9 в одном сценарии

Представим таблицы users и orders (упрощённо).

Правило 2 (гарантированный доступ). Запрос email пользователя 42:

SELECT email FROM users WHERE id = 42;

Логический путь: отношение users → ключ 42 → атрибут email. Оптимизатор может идти через индекс по id, но смысл запроса не меняется — это и есть независимость от физики (правило 8).

Правило 9 (логическая независимость). Бизнес просит разделить ФИО на first_name и last_name, но старый микросервис ещё ждёт столбец full_name:

CREATE VIEW users_legacy AS
SELECT id,
first_name || ' ' || last_name AS full_name,
email
FROM users;

Старый код читает users_legacy; новый — напрямую users. После отключения всех клиентов view удаляют — классический Expand–Contract, описанный в роли БД в организации.

Правило 6 (обновляемые представления). View только с колонками из одной таблицы users без агрегации — в PostgreSQL часто updatable. View с JOIN users и orders и SUM(amount) — для автообновления не подходит; отчёт остаётся read-only или обновляется через хранимую процедуру / API.


Насколько «реляционны» PostgreSQL, MySQL, SQL Server?

ПравилоТипичная оценка для зрелой РСУБД
1–5, 7–10Выполняются в повседневной работе через SQL
6Частично: не все VIEW обновляемы
11Зависит от топологии (один узел — да; шард без прозрачности — нет)
12Нарушается при прямом вмешательстве в файлы данных

Ни один продукт обычно не сертифицируют «на 12 из 12» — правила Кодда остаются ориентиром для архитекторов и тендеров.


Связь с тем, что вы уже прошли


Контрольные вопросы

  1. Чем правило 8 (физическая) отличается от правила 9 (логическая) независимости?
  2. Почему правило 4 делает information_schema частью реляционной модели?
  3. Приведите пример VIEW, которое, скорее всего, нельзя обновлять (правило 6).
  4. Как правило 12 связано с запретом «чинить» PostgreSQL копированием каталога данных вручную?
  5. Почему отсутствие DISTINCT в SQL не отменяет реляционную модель, но влияет на проектирование?
  6. Назовите два способа смягчить проблему «перегруженного» NULL на этапе схемы.

См. также


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).