Нормализация

Материал из llmdocs — справочник разработчика
Портал: Базы данных
Нормализация
Автор теорииЭдгар Кодд (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]

См. также

Источники

  1. E. F. Codd — A Relational Model of Data for Large Shared Data Banks (CACM, 1970)
  2. W. Kent — A Simple Guide to Five Normal Forms in Relational Database Theory (1983)
  3. R. Fagin — Multivalued Dependencies and a New Normal Form for Relational Databases (1977)
  4. Microsoft Learn — Description of the database normalization basics
  5. PostgreSQL Documentation — Constraints