← Назад к вопросам

Что такое нормализация в БД?

2.2 Middle🔥 241 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Нормализация в базах данных

Нормализация — это процесс организации данных в таблицы БД чтобы минимизировать избыточность и улучшить целостность данных. Это ключевой концепт RDBMS.

Зачем нужна нормализация

Проблемы ненормализованных данных:

  • Дублирование данных — одна информация хранится несколько раз
  • Аномалии обновления — изменяю один экземпляр, забываю про другие
  • Аномалии удаления — удаляю запись, теряю другую информацию
  • Аномалии вставки — не могу добавить данные без всей информации

Пример: Ненормализованная таблица

Students:
┌─────┬───────────┬──────────────────────────┬────────────────┐
│ id  │ name      │ courses                  │ professors     │
├─────┼───────────┼──────────────────────────┼────────────────┤
│ 1   │ Ivan      │ Math, Physics            │ Prof.A, Prof.B │
│ 2   │ Maria     │ Math, Chemistry          │ Prof.A, Prof.C │
│ 3   │ Sergei    │ Physics, Chemistry       │ Prof.B, Prof.C │
└─────┴───────────┴──────────────────────────┴────────────────┘

Проблемы:

  • Если обновить имя Professor.A, нужно обновить несколько строк
  • Если удалить студента Ivan, потеряю информацию про курсы
  • Поиск по курсу требует разбора строк

Нормальные формы (Normal Forms)

1NF (Первая нормальная форма)

Правило: Каждая ячейка содержит только одно значение (атомарное).

НЕ нормализовано (1NF нарушена):
students:
┌──┬────────┬─────────────────────┐
│id│ name   │ courses             │
├──┼────────┼─────────────────────┤
│ 1│ Ivan   │ Math, Physics       │  ← Несколько значений
│ 2│ Maria  │ Math, Chemistry     │
└──┴────────┴─────────────────────┘

Нормализовано (1NF):
students:
┌──┬────────┐
│id│ name   │
├──┼────────┤
│ 1│ Ivan   │
│ 2│ Maria  │
└──┴────────┘

enrollments:
┌──────────────┬────────┐
│ student_id   │course  │
├──────────────┼────────┤
│ 1            │ Math   │
│ 1            │Physics │
│ 2            │ Math   │
│ 2            │Chemistry│
└──────────────┴────────┘

2NF (Вторая нормальная форма)

Правило: Таблица в 1NF И все неключевые атрибуты полностью зависят от первичного ключа (не от его части).

НЕ нормализовано (2NF нарушена):
student_courses:
┌────┬──────────┬────────────┬──────────────┐
│sid │ cid      │ student_name│ course_name  │
├────┼──────────┼────────────┼──────────────┤
│ 1  │ 10       │ Ivan       │ Math         │  ← student_name зависит
│ 1  │ 20       │ Ivan       │ Physics      │    только от sid
│ 2  │ 10       │ Maria      │ Math         │
└────┴──────────┴────────────┴──────────────┘

Проблема: если обновить имя Ivan в первой строке, вторая строка станет несогласованной.

Нормализовано (2NF):
students:
┌────┬──────────┐
│sid │ name     │
├────┼──────────┤
│ 1  │ Ivan     │
│ 2  │ Maria    │
└────┴──────────┘

courses:
┌────┬───────────┐
│cid │ name      │
├────┼───────────┤
│ 10 │ Math      │
│ 20 │ Physics   │
└────┴───────────┘

enrollments:
┌──────────┬────────┐
│student_id│course_id│
├──────────┼────────┤
│ 1        │ 10     │
│ 1        │ 20     │
│ 2        │ 10     │
└──────────┴────────┘

3NF (Третья нормальная форма)

Правило: Таблица в 2NF И все неключевые атрибуты зависят ТОЛЬКО от первичного ключа (не от других неключевых атрибутов).

НЕ нормализовано (3NF нарушена):
students:
┌────┬──────────┬──────────┬──────────────┐
│id  │ name     │ city_id  │ city_name    │
├────┼──────────┼──────────┼──────────────┤
│ 1  │ Ivan     │ 1        │ Moscow       │  ← city_name зависит
│ 2  │ Maria    │ 1        │ Moscow       │    от city_id (не от id)
│ 3  │ Sergei   │ 2        │ SPB          │
└────┴──────────┴──────────┴──────────────┘

Проблема: Если переименовать город Moscow, нужно обновить все студенты.

Нормализовано (3NF):
cities:
┌───┬─────────┐
│id │ name    │
├───┼─────────┤
│ 1 │ Moscow  │
│ 2 │ SPB     │
└───┴─────────┘

students:
┌────┬──────────┬────────┐
│id  │ name     │city_id │
├────┼──────────┼────────┤
│ 1  │ Ivan     │ 1      │
│ 2  │ Maria    │ 1      │
│ 3  │ Sergei   │ 2      │
└────┴──────────┴────────┘

BCNF (Boyce-Codd Normal Form)

Правило: Ещё более строгая 3NF. Каждый детерминант должен быть кандидатом на первичный ключ.

Для большинства приложений 3NF достаточно.

4NF и 5NF

Очень редко используются. Нужны только для особых случаев.

SQL примеры нормализации

-- До нормализации (плохо)
CREATE TABLE students_bad (
  id INT PRIMARY KEY,
  name VARCHAR(100),
  courses VARCHAR(500),  -- Несколько курсов
  professor_names VARCHAR(500)  -- Несколько профессоров
);

-- После нормализации (хорошо)
CREATE TABLE students (
  id INT PRIMARY KEY,
  name VARCHAR(100) NOT NULL
);

CREATE TABLE courses (
  id INT PRIMARY KEY,
  name VARCHAR(100) NOT NULL
);

CREATE TABLE professors (
  id INT PRIMARY KEY,
  name VARCHAR(100) NOT NULL
);

CREATE TABLE enrollments (
  student_id INT REFERENCES students(id),
  course_id INT REFERENCES courses(id),
  professor_id INT REFERENCES professors(id),
  PRIMARY KEY (student_id, course_id)
);

Денормализация: когда нарушать правила

Иногда нормализация замедляет запросы. Тогда используют денормализацию:

-- Нормализовано: много JOIN
SELECT s.name, c.name, p.name
FROM students s
JOIN enrollments e ON s.id = e.student_id
JOIN courses c ON e.course_id = c.id
JOIN professors p ON e.professor_id = p.id;

-- Денормализовано: кэшируем часто запрашиваемые данные
CREATE TABLE enrollments_denorm (
  id INT PRIMARY KEY,
  student_id INT,
  student_name VARCHAR(100),  -- Кэш
  course_id INT,
  course_name VARCHAR(100),   -- Кэш
  professor_id INT,
  professor_name VARCHAR(100), -- Кэш
  created_at TIMESTAMP
);

Когда денормализовать:

  • Запросы очень медленные
  • Данные редко меняются
  • Reads гораздо больше чем writes
  • Нужна аналитика с большими таблицами

Правило: Normalise until it hurts, denormalise until it helps (нормализуй пока не болит, денормализуй пока не поможет).

Типичные ошибки

  1. Слишком много JOIN — может быть это сигнал денормализации
  2. Большие VARCHAR для кодов — используй INT с FK
  3. Дублирование фиксированных данных — используй справочники
  4. Нет индексов на FK — добавляй индексы на внешние ключи

Практический пример: E-commerce

-- Правильно нормализовано

-- Каталог
CREATE TABLE categories (
  id INT PRIMARY KEY,
  name VARCHAR(100)
);

CREATE TABLE products (
  id INT PRIMARY KEY,
  name VARCHAR(200),
  category_id INT REFERENCES categories(id),
  price DECIMAL(10, 2)
);

-- Заказы
CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT REFERENCES customers(id),
  order_date TIMESTAMP,
  status VARCHAR(20)
);

-- Позиции в заказе
CREATE TABLE order_items (
  id INT PRIMARY KEY,
  order_id INT REFERENCES orders(id),
  product_id INT REFERENCES products(id),
  quantity INT,
  price DECIMAL(10, 2),  -- Фиксируем цену на момент заказа
  UNIQUE(order_id, product_id)
);

-- Скидки (если нужны)
CREATE TABLE discounts (
  id INT PRIMARY KEY,
  code VARCHAR(50) UNIQUE,
  percentage INT
);

Инструменты для проверки нормализации

  1. Нарисовать ER диаграмму — помогает увидеть отношения
  2. Проверить каждое поле — зависит ли от ключа?
  3. Проверить аномалии — Insert/Update/Delete работают корректно?
  4. Использовать EXPLAIN ANALYZE — видны ли JOIN'ы и количество сканов

Нормализация — это баланс между консистентностью данных и производительностью. На практике я нормализую до 3NF, потом смотрю на production queries и денормализую если нужно.

Что такое нормализация в БД? | PrepBro