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

Что такое нормализация данных в контексте баз данных? Какие формы нормализации существуют?

1.8 Middle🔥 141 комментариев
#SQL и базы данных

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

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

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

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

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

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

Проблемы без нормализации:

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

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

Таблица: Orders
┌─────┬────────────┬──────────────┬─────────────┬─────────────┐
│ ID  │ Customer   │ Product1     │ Product2    │ Product3    │
├─────┼────────────┼──────────────┼─────────────┼─────────────┤
│ 1   │ Иван       │ Ноутбук ($) │ Мышь ($)    │ Клавиатура($)│
│ 2   │ Мария      │ Монитор ($)  │ NULL        │ NULL        │
└─────┴────────────┴──────────────┴─────────────┴─────────────┘

Проблемы:

  • Дублирование: Иван и Мария видны только в этой таблице
  • Растяжение таблицы: если продуктов больше 3, нужны новые колонки
  • Аномалия удаления: удалив заказ Марии, потеряем информацию о ней

Нормальные формы

В теории есть 5+ нормальных форм, но на практике используют первые 3:

1NF (First Normal Form) — Атомарность

Правило: каждая ячейка таблицы содержит только одно значение (нет массивов, списков, вложенных структур).

Ненормализовано:

┌─────┬────────┬──────────────────────────┐
│ ID  │ Name   │ Phones                   │
├─────┼────────┼──────────────────────────┤
│ 1   │ Иван   │ 123-45-67, 789-01-23    │
└─────┴────────┴──────────────────────────┘

Нормализовано (1NF):

┌─────┬────────┐     ┌─────┬───────────┐
│ ID  │ Name   │     │ ID  │ Phone     │
├─────┼────────┤     ├─────┼───────────┤
│ 1   │ Иван   │     │ 1   │ 123-45-67 │
└─────┴────────┘     │ 1   │ 789-01-23 │
                     └─────┴───────────┘

Преимущество: каждое значение занимает одну ячейку, упрощает запросы.

2NF (Second Normal Form) — Зависимость от всего ключа

Правило: все неключевые атрибуты должны полностью зависеть от всего составного первичного ключа, а не от его части.

Ненормализовано:

Таблица: StudentClasses (первичный ключ: StudentID + ClassID)
┌───────────┬─────────┬──────────────┬────────────────┐
│StudentID  │ ClassID │ ClassName    │ StudentName    │
├───────────┼─────────┼──────────────┼────────────────┤
│ 1         │ 101     │ Math         │ Иван           │
│ 1         │ 102     │ Physics      │ Иван           │
└───────────┴─────────┴──────────────┴────────────────┘

Проблема: StudentName зависит только от StudentID, а не от (StudentID + ClassID). ClassName зависит только от ClassID, а не от (StudentID + ClassID).

Нормализовано (2NF):

Таблица: Students
┌───────────┬────────────┐
│ StudentID │ StudentName│
├───────────┼────────────┤
│ 1         │ Иван       │
└───────────┴────────────┘

Таблица: Classes
┌─────────┬──────────────┐
│ ClassID │ ClassName    │
├─────────┼──────────────┤
│ 101     │ Math         │
│ 102     │ Physics      │
└─────────┴──────────────┘

Таблица: Enrollments
┌───────────┬─────────┐
│ StudentID │ ClassID │
├───────────┼─────────┤
│ 1         │ 101     │
│ 1         │ 102     │
└───────────┴─────────┘

Преимущество: нет дублирования данных. StudentName хранится один раз.

3NF (Third Normal Form) — Отсутствие транзитивных зависимостей

Правило: все неключевые атрибуты должны зависеть ТОЛЬКО от первичного ключа, а не друг от друга.

Ненормализовано:

Таблица: Orders
┌──────┬─────────────┬──────────┬────────────────┐
│ ID   │ CustomerID  │ CityID   │ CityName       │
├──────┼─────────────┼──────────┼────────────────┤
│ 1    │ 101         │ 1        │ Москва         │
│ 2    │ 102         │ 1        │ Москва         │
└──────┴─────────────┴──────────┴────────────────┘

Проблема: CityName зависит от CityID, а CityID уже хранится в таблице. Это транзитивная зависимость.

Нормализовано (3NF):

Таблица: Orders
┌──────┬─────────────┬──────────┐
│ ID   │ CustomerID  │ CityID   │
├──────┼─────────────┼──────────┤
│ 1    │ 101         │ 1        │
│ 2    │ 102         │ 1        │
└──────┴─────────────┴──────────┘

Таблица: Cities
┌────────┬────────────┐
│ CityID │ CityName   │
├────────┼────────────┤
│ 1      │ Москва     │
└────────┴────────────┘

Преимущество: городское имя хранится в одном месте. Если его изменить, обновляем только Cities.

Сравнение форм нормализации

ФормаФокусРешает проблему
1NFАтомарность значенийМассивы в ячейках
2NFПолная зависимость от ключаЧастичные зависимости
3NFНет транзитивных зависимостейЗависимости между неключевыми атрибутами
BCNFСтрогая форма 3NFРедкие случаи с составными ключами

Практический пример: Интернет-магазин

Неправильно (денормализовано):

Orders: [ID, CustomerName, CustomerEmail, CustomerCity, 
         ProductName, ProductPrice, ProductCategory, ...]

Проблема: Петя из Москвы, купивший 5 товаров = 5 строк с повторением имени, email, города.

Правильно (нормализовано до 3NF):

Customers:
- CustomerID, Name, Email, CityID

Cities:
- CityID, CityName

Products:
- ProductID, Name, Price, CategoryID

Categories:
- CategoryID, Name

Orders:
- OrderID, CustomerID, OrderDate

OrderItems:
- OrderItemID, OrderID, ProductID, Quantity, UnitPrice

Теперь:

  • Петя хранится один раз в Customers
  • Товары хранятся один раз в Products
  • Один заказ = одна строка в Orders + по строке в OrderItems для каждого товара

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

В аналитических БД (OLAP, Data Warehouse) часто специально денормализуют для скорости чтения.

Пример: вместо множества JOIN, создают таблицу фактов:

-- В OLTP (нормализовано):
SELECT o.id, c.name, p.name, SUM(oi.quantity)
FROM Orders o
JOIN OrderItems oi ON o.id = oi.order_id
JOIN Products p ON oi.product_id = p.id
JOIN Customers c ON o.customer_id = c.id
GROUP BY o.id, c.name, p.name

-- В OLAP (денормализовано):
SELECT order_id, customer_name, product_name, quantity
FROM fact_orders
-- Один SELECT, без JOIN, быстрее!

Денормализация имеет смысл когда:

  • Таблица только для чтения (Data Warehouse)
  • Скорость чтения критична
  • Данные редко обновляются

Контрольный список для проверки нормализации

1NF:

  • Нет повторяющихся групп
  • Каждая ячейка содержит одно значение
  • Нет массивов/списков в ячейках

2NF:

  • Соответствует 1NF
  • Все неключевые атрибуты зависят от ВСЕГО первичного ключа
  • Нет частичных зависимостей

3NF:

  • Соответствует 2NF
  • Все неключевые атрибуты зависят только от первичного ключа
  • Нет транзитивных зависимостей

Итог

  • Нормализация минимизирует дублирование и избегает аномалий
  • 1NF, 2NF, 3NF покрывают 99% реальных случаев
  • На практике: используй 3NF для OLTP (операционных) БД, денормализуй для аналитики
  • Tools: используй диаграммы ER (Entity-Relationship) для проектирования
  • Баланс: идеальная нормализация может замедлить чтение. Иногда стоит немного денормализовать для скорости
Что такое нормализация данных в контексте баз данных? Какие формы нормализации существуют? | PrepBro