Нормализация
| Автор теории | Эдгар Кодд (1970–1971) |
|---|---|
| Основные формы | 1НФ, 2НФ, 3НФ, нормальная форма Бойса — Кодда (НФБК) |
| Высшие формы | 4НФ (Фейгин, 1977), 5НФ (1979), 6НФ |
| Цель | устранить дублирование и аномалии вставки, обновления, удаления |
| Ключевое понятие | функциональная зависимость |
| Обратный процесс | денормализация |
Нормализация — процесс проектирования схемы реляционной базы данных, при котором таблицы разбиваются так, чтобы каждый факт хранился ровно в одном месте. Это устраняет дублирование данных и связанные с ним аномалии: когда изменение приходится делать в нескольких местах, а забытое место оставляет базу в противоречивом состоянии. Уровни нормализованности называются нормальными формами; теорию заложил Эдгар Кодд в 1970–1971 годах.[1]
Обратный шаг — денормализация — сознательное дублирование данных ради скорости чтения.
Проблема: аномалии
Рассмотрим таблицу заказов, в которой всё хранится вместе:
| order_id | customer | customer_phone | product | category | price | qty |
|---|---|---|---|---|---|---|
| 1 | Алиса | +7 900 111-11-11 | Ноутбук | Электроника | 90 000 | 1 |
| 1 | Алиса | +7 900 111-11-11 | Мышь | Электроника | 1 500 | 2 |
| 2 | Борис | +7 900 222-22-22 | Мышь | Электроника | 1 500 | 1 |
- Аномалия обновления: Алиса сменила телефон — его нужно исправить во всех её строках; пропущенная строка создаст противоречие.
- Аномалия вставки: нельзя добавить новый товар, пока его никто не заказал.
- Аномалия удаления: удалив единственный заказ Бориса, мы потеряем и его телефон.
Функциональные зависимости
Нормальные формы формулируются через функциональные зависимости: столбец B зависит от A, если значение A однозначно определяет B. Здесь customer → customer_phone, product → category, price, а (order_id, product) → qty. Нормализация приводит таблицы к виду, где каждый неключевой столбец зависит от ключа — «от ключа, от всего ключа и ни от чего, кроме ключа».
Нормальные формы
| Форма | Требование | Нарушение в примере |
|---|---|---|
| 1НФ | все значения атомарны, нет повторяющихся групп и списков в ячейке | столбец «товары» со списком через запятую |
| 2НФ | 1НФ, и неключевые столбцы зависят от всего составного ключа | price зависит только от product, а не от пары (order_id, product) |
| 3НФ | 2НФ, и нет транзитивных зависимостей неключевых столбцов друг от друга | customer_phone зависит от customer, а не от ключа заказа |
| НФБК | каждый детерминант — потенциальный ключ | редкие случаи с перекрывающимися ключами |
| 4НФ, 5НФ | нет многозначных зависимостей и зависимостей соединения | несколько независимых списков в одной таблице |
Четвёртую форму ввёл Рональд Фейгин в 1977 году;[3] понятное объяснение всех пяти форм дал Уильям Кент в статье «Простое руководство по пяти нормальным формам».[2] На практике чаще всего стремятся к третьей нормальной форме или НФБК.
Нормализованная схема
CREATE TABLE customers (
id bigint PRIMARY KEY,
name text NOT NULL,
phone text
);
CREATE TABLE products (
id bigint PRIMARY KEY,
name text NOT NULL UNIQUE,
category text NOT NULL,
price numeric(12, 2) NOT NULL CHECK (price >= 0)
);
CREATE TABLE orders (
id bigint PRIMARY KEY,
customer_id bigint NOT NULL REFERENCES customers(id),
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE order_items (
order_id bigint NOT NULL REFERENCES orders(id),
product_id bigint NOT NULL REFERENCES products(id),
qty int NOT NULL CHECK (qty > 0),
unit_price numeric(12, 2) NOT NULL, -- цена на момент покупки
PRIMARY KEY (order_id, product_id)
);
Телефон хранится в одном месте, товар существует независимо от заказов, а связи обеспечиваются внешними ключами.[5] Обратите внимание на unit_price: это не дублирование, а другой факт — цена в момент заказа, которая не должна меняться вместе с текущей ценой товара.
Денормализация
Нормализованная схема требует соединений (JOIN) при чтении, и на больших объёмах или в горячих запросах это может стать узким местом. Тогда часть данных дублируют сознательно:
- хранят вычисляемые итоги (сумма заказа, число комментариев) рядом с сущностью;
- копируют редко меняющиеся атрибуты, чтобы избежать соединения;
- строят материализованные представления и витрины для отчётов;
- в аналитических хранилищах используют схему «звезда» с широкими таблицами фактов (см. Колоночное хранение).
Денормализация переносит ответственность за согласованность на приложение, триггеры или процессы обновления, поэтому её применяют точечно — после того как узкое место подтверждено измерениями (EXPLAIN и план запроса), а не заранее.
Нормализация и NoSQL
Документные базы (MongoDB и другие NoSQL-системы) часто хранят связанные данные вложенными документами — по сути, денормализованно, — оптимизируя под конкретные сценарии чтения. Это удобно, когда данные читаются целиком и редко меняются по частям, но аномалии обновления никуда не исчезают: их просто приходится решать в коде.
Практические советы
- Начинайте с нормализованной схемы (3НФ) и денормализуйте по результатам измерений.
- Выражайте правила ограничениями —
PRIMARY KEY,UNIQUE,FOREIGN KEY,CHECK— чтобы база сама защищала целостность. - Не храните списки в одной ячейке через запятую — для связи «многие ко многим» нужна отдельная таблица.
- Изменения схемы проводите миграциями. Краткое введение в основы нормализации есть в документации Microsoft.[4]
См. также
Источники
- E. F. Codd — A Relational Model of Data for Large Shared Data Banks (CACM, 1970)
- W. Kent — A Simple Guide to Five Normal Forms in Relational Database Theory (1983)
- R. Fagin — Multivalued Dependencies and a New Normal Form for Relational Databases (1977)
- Microsoft Learn — Description of the database normalization basics
- PostgreSQL Documentation — Constraints