Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Нормализация в базах данных
Нормализация — это процесс организации данных в таблицы БД чтобы минимизировать избыточность и улучшить целостность данных. Это ключевой концепт 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 (нормализуй пока не болит, денормализуй пока не поможет).
Типичные ошибки
- Слишком много JOIN — может быть это сигнал денормализации
- Большие VARCHAR для кодов — используй INT с FK
- Дублирование фиксированных данных — используй справочники
- Нет индексов на 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
);
Инструменты для проверки нормализации
- Нарисовать ER диаграмму — помогает увидеть отношения
- Проверить каждое поле — зависит ли от ключа?
- Проверить аномалии — Insert/Update/Delete работают корректно?
- Использовать EXPLAIN ANALYZE — видны ли JOIN'ы и количество сканов
Нормализация — это баланс между консистентностью данных и производительностью. На практике я нормализую до 3NF, потом смотрю на production queries и денормализую если нужно.