Аномалии и нормальные формы 1NF, 2NF, 3NF
Плохо спроектированная таблица наказывает аномалиями при каждом изменении данных. Нормальные формы — это лекарство. Разберём 1NF, 2NF и 3NF строго.
Нормализация — это процесс декомпозиции отношений на основе функциональных зависимостей так, чтобы устранить избыточность и аномалии, сохранив возможность восстановить исходные данные соединением.
Зачем нормализовать: аномалии
Вернёмся к «плоской» таблице, где в каждой строке заказа продублированы данные клиента и товара: (order_id, product_id, client_name, client_city, product_name, cena, qty). Хранить всё в одной таблице кажется удобным, но избыточность мстит тремя видами аномалий.
- Аномалия обновления. Клиент сменил город. Его имя и город продублированы в каждой строке его заказов — нужно обновить десятки строк. Пропустили одну — данные противоречивы.
- Аномалия вставки. Хотим завести новый товар, который ещё никто не заказал. Но строка существует только в составе заказа — товар «без заказа» вставить некуда (или придётся ставить NULL в ключевые поля).
- Аномалия удаления. Удалили единственный заказ с этим товаром — и вместе с ним потеряли название и цену товара, хотя сам товар никуда не делся.
Все три аномалии — следствие того, что в одной таблице смешаны факты о разных сущностях. Нормализация разносит их по отдельным таблицам.
Что такое нормализация и зачем она пошагова
Нормализация устроена как лестница: 1NF, затем 2NF, затем 3NF, затем BCNF — каждая форма строже предыдущей и устраняет свой класс проблем. Почему не сразу к самой строгой? Потому что пошаговость делает процесс понятным и управляемым: на каждой ступени мы убираем один конкретный вид зависимости и точно знаем, что именно исправили. К тому же не всегда нужна самая строгая форма — иногда 3NF достаточна (об этом в уроке про BCNF). Важно и то, что нормализация — это не «разрезать таблицу как попало», а декомпозиция без потерь: разбивая отношение, мы обязаны иметь возможность соединением восстановить ровно исходные данные. Разрезать так, чтобы соединение давало лишние или, наоборот, терять строки, — недопустимо. Поэтому за каждой нормальной формой стоит не только «что устранить», но и «как разбить корректно».
Первая нормальная форма (1NF)
Отношение в 1NF, если все его атрибуты атомарны: в каждой ячейке ровно одно неделимое значение, нет повторяющихся групп и вложенных таблиц. По сути, 1NF — это требование быть настоящим отношением. Нарушение — хранить список телефонов через запятую в одном поле или повторять колонки product1, product2, product3. Лечение: вынести многозначные данные в отдельные строки/таблицу.
Аномалии — это симптом, избыточность — болезнь
Полезно зафиксировать причинно-следственную связь, потому что она объясняет всю нормализацию. Корень зла — избыточность: один и тот же факт (имя клиента, цена товара) физически хранится в нескольких строках. Аномалии — лишь её симптомы. Аномалия обновления возникает, потому что копий много и их легко рассогласовать. Аномалия удаления — потому что факт о товаре «прицеплен» к строке заказа и гибнет вместе с ней. Аномалия вставки — потому что факт нельзя записать отдельно от строки, в которую он вшит. Уберите избыточность (храните каждый факт один раз) — и все три симптома исчезнут разом, без отдельной борьбы с каждым. Именно это делает нормализация: она не «лечит аномалии» по одной, а устраняет их общую причину. Поэтому, увидев в схеме дублирующийся факт, можно заранее предсказать все три аномалии — они придут неизбежно.
Вторая нормальная форма (2NF)
2NF имеет смысл только когда ключ составной. Отношение в 2NF, если оно в 1NF и каждый непервичный атрибут функционально зависит от всего потенциального ключа целиком, а не от его части. Нарушение называется частичной зависимостью.
В нашей таблице ключ — {order_id, product_id}. Но client_name зависит только от order_id (часть ключа), а product_name и cena — только от product_id (другая часть ключа). Это частичные зависимости — 2NF нарушена. Именно они порождают аномалии вставки/удаления товара. Лечение: вынести «частично зависящие» атрибуты в отдельные таблицы по их детерминантам.
| Таблица | Атрибуты | Ключ |
| order_items | order_id, product_id, qty | (order_id, product_id) |
| orders | order_id, client_name, client_city | order_id |
| products | product_id, product_name, cena | product_id |
Теперь qty зависит от всего ключа связующей таблицы, а данные товара и заказа живут отдельно. Аномалии вставки и удаления товара исчезли.
Третья нормальная форма (3NF)
2NF убрала зависимости от части ключа, но осталась другая беда — зависимости неключевых атрибутов друг от друга. В таблице orders(order_id, client_id, client_city): order_id → client_id и client_id → client_city, откуда транзитивно order_id → client_city. Город зависит от заказа не напрямую, а через клиента — это транзитивная зависимость, и она снова даёт аномалии (город клиента продублирован во всех его заказах).
Строгое определение: отношение в 3NF, если оно в 2NF и для каждой нетривиальной ФЗ X → A выполнено хотя бы одно из условий: X — суперключ, или A — первичный (prime) атрибут (входит в какой-либо потенциальный ключ). Эквивалентно: ни один непервичный атрибут не зависит транзитивно от ключа.
В orders ФЗ client_id → client_city нарушает 3NF: client_id — не суперключ, а client_city — не первичный атрибут. Лечение: вынести клиента в свою таблицу.
-- после нормализации до 3NF: три чистые таблицы
CREATE TABLE clients (id INTEGER PRIMARY KEY, name TEXT, city TEXT);
CREATE TABLE orders (id INTEGER PRIMARY KEY, client_id INTEGER REFERENCES clients(id));
CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, cena INTEGER);
CREATE TABLE order_items (
order_id INTEGER REFERENCES orders(id),
product_id INTEGER REFERENCES products(id),
qty INTEGER,
PRIMARY KEY (order_id, product_id)
);
INSERT INTO clients VALUES (1,'Анна','Москва');
INSERT INTO orders VALUES (10,1),(11,1);
INSERT INTO products VALUES (1,'Книга',500);
INSERT INTO order_items VALUES (10,1,2),(11,1,1);
-- город клиента хранится ОДИН раз; меняем в одном месте
UPDATE clients SET city = 'Казань' WHERE id = 1;
SELECT o.id AS zakaz, c.name, c.city FROM orders o JOIN clients c ON c.id = o.client_id;
Вывод: «10, Анна, Казань» и «11, Анна, Казань». Мы поменяли город одной строкой в clients — и оба заказа сразу видят новое значение. Аномалия обновления побеждена.
Общая идея всех нормальных форм
За частными определениями 2NF и 3NF стоит одна объединяющая мысль, которую полезно увидеть: каждый факт должен храниться ровно в одном месте, и определяться он должен ключом, всем ключом и ничем, кроме ключа. Эта знаменитая формула («the key, the whole key, and nothing but the key») кратко выражает 2NF и 3NF вместе. «Всем ключом» — это 2NF: атрибут не должен зависеть лишь от части составного ключа (иначе он относится не ко всей строке, а к её части). «Ничем, кроме ключа» — это 3NF: атрибут не должен зависеть от другого неключевого атрибута (иначе он на самом деле описывает не эту сущность, а ту, через которую зависит). Когда каждый неключевой атрибут зависит точно от ключа целиком и больше ни от чего, избыточности неоткуда взяться — а значит, неоткуда взяться и аномалиям. Все нормальные формы — лишь разные грани этого единого принципа.
Порядок и смысл нормальных форм
| Форма | Что устраняет |
| 1NF | Неатомарные значения, повторяющиеся группы |
| 2NF | Частичные зависимости (от части составного ключа) |
| 3NF | Транзитивные зависимости (неключевой через неключевой) |
Формы вложены: каждая следующая включает предыдущую. Достичь 3NF, минуя 1NF, нельзя.
Типичные ошибки
- Список значений в одной ячейке. Нарушение 1NF; «теги через запятую» убивают поиск и целостность.
- Атрибут зависит от части ключа. Нарушение 2NF; данные товара в строке заказа — классика.
- Неверная формулировка 3NF. Условие «A — первичный атрибут» забывают; без него определение слишком строгое и совпало бы с BCNF.
- Нормализация без ФЗ. Без анализа зависимостей нормализация превращается в гадание.
Итог
- Избыточность порождает аномалии вставки, обновления и удаления — их и убирает нормализация.
- 1NF требует атомарности значений; 2NF убирает частичные зависимости от составного ключа.
- 3NF убирает транзитивные зависимости: для каждой нетривиальной ФЗ X → A детерминант X — суперключ или A — первичный атрибут.
- Нормальные формы вложены и опираются на корректно выявленные функциональные зависимости.