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

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

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

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

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

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

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

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

Основная идея

Представьте таблицу студентов с их курсами:

Студент  | Курс 1        | Курс 2        | Курс 3
---------|---------------|---------------|---------------
Алиса    | Математика    | Физика        | Химия
Боб      | Математика    | Программирование | NULL
Чарли    | Физика        | NULL          | NULL

Проблемы:

  • Если Боб добавит ещё курс, нужно добавить Курс 4 для других
  • Сложно обновлять информацию о курсах
  • Много NULL значений

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

Таблица Students:
id | name
---|------
1  | Алиса
2  | Боб
3  | Чарли

Таблица Courses:
id | name
---|------------------
1  | Математика
2  | Физика
3  | Химия
4  | Программирование

Таблица Enrollments (связная):
student_id | course_id
-----------|----------
1          | 1
1          | 2
1          | 3
2          | 1
2          | 4
3          | 2

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

Нормализация определяется несколькими нормальными формами (NF). Каждая следующая форма требует выполнения предыдущей.

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

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

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

CREATE TABLE students (
    id INT,
    name VARCHAR(100),
    courses VARCHAR(200)  -- "Математика, Физика, Химия" — не атомарно!
);

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

CREATE TABLE students (
    id INT PRIMARY KEY,
    name VARCHAR(100)
);

CREATE TABLE enrollments (
    student_id INT REFERENCES students(id),
    course_id INT REFERENCES courses(id)
);

2NF (Second Normal Form) — Зависимость от Primary Key

Правило: Все non-key атрибуты должны зависеть от всего primary key, а не от его части.

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

CREATE TABLE student_courses (
    student_id INT,
    course_id INT,
    course_name VARCHAR(100),  -- зависит только от course_id, не от student_id!
    PRIMARY KEY (student_id, course_id)
);

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

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

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

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

Правило: Non-key атрибуты не должны зависеть друг от друга. Все зависимости должны быть от primary key.

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

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    customer_name VARCHAR(100),  -- зависит от customer_id
    customer_city VARCHAR(100)   -- зависит от customer_id, не от order_id!
);

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

CREATE TABLE customers (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    city VARCHAR(100)
);

CREATE TABLE orders (
    id INT PRIMARY KEY,
    customer_id INT REFERENCES customers(id)
);

Практический пример: денормализация

Представьте интернет-магазин:

Денормализованная структура (3 таблицы в одной):

CREATE TABLE sales (
    id INT PRIMARY KEY,
    product_id INT,
    product_name VARCHAR(100),    -- дублируется
    product_price DECIMAL(10,2),  -- дублируется
    customer_id INT,
    customer_name VARCHAR(100),    -- дублируется
    customer_email VARCHAR(100),   -- дублируется
    quantity INT,
    total DECIMAL(10,2)           -- можно вычислить
);

Проблемы:

  • Если изменилось имя товара, нужно обновить все строки
  • Дублирование данных (5MB × 1000000 строк = 5GB лишних данных)
  • Сложный UPDATE ("UPDATE sales SET product_name = ...")
  • Аномалии удаления (удалим последний заказ — потеряем данные о товаре)

Нормализованная структура:

CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10,2)
);

CREATE TABLE customers (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100)
);

CREATE TABLE sales (
    id INT PRIMARY KEY,
    product_id INT REFERENCES products(id),
    customer_id INT REFERENCES customers(id),
    quantity INT
);

Преимущества:

  • Обновить название товара: одна строка в products
  • Меньше памяти (только ключи)
  • SELECT с JOINами все равно быстрый

Аномалии данных (что решает нормализация)

1. Insert Anomaly (аномалия вставки):

Нельзя добавить нового товара без заказа

2. Update Anomaly (аномалия обновления):

Если изменился email клиента, нужно обновить все его заказы

3. Delete Anomaly (аномалия удаления):

Если удалить последний заказ клиента, потеряем информацию о клиенте

BCNF (Boyce-Codd Normal Form) — строгая версия 3NF

Правило: Каждый детерминант должен быть candidate key.

Осложнённый пример (редко встречается на практике):

-- Ненормализованный пример
CREATE TABLE professor_courses (
    professor_id INT,
    course_id INT,
    time_slot INT,
    PRIMARY KEY (professor_id, course_id)
);

-- Проблема: time_slot может зависеть от course_id, а не от professor_id

-- Нормализованный
CREATE TABLE course_schedules (
    course_id INT PRIMARY KEY,
    time_slot INT
);

CREATE TABLE professor_courses (
    professor_id INT,
    course_id INT REFERENCES course_schedules(course_id),
    PRIMARY KEY (professor_id, course_id)
);

Денормализация: когда это необходимо

Порой денормализация оправдана для производительности:

-- Вместо 3 JOINов при каждом SELECT
SELECT 
    s.id,
    p.name,
    c.name,
    s.quantity
FROM sales s
JOIN products p ON s.product_id = p.id
JOIN customers c ON s.customer_id = c.id;

-- Можно хранить денормализованный view или кэш
CREATE MATERIALIZED VIEW sales_denormalized AS
SELECT 
    s.id,
    p.name as product_name,
    c.name as customer_name,
    s.quantity
FROM sales s
JOIN products p ON s.product_id = p.id
JOIN customers c ON s.customer_id = c.id;

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

  • Запрос часто вызывается (OLAP, аналитика)
  • Много тяжёлых JOINов
  • Данные меняются редко
  • Кэш обновляется периодически

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

import sqlalchemy as sa
from sqlalchemy import inspect

# Проверить структуру таблицы
inspector = inspect(engine)
columns = inspector.get_columns(students)
for col in columns:
    print(f"{col[name]}: {col[type]}")

# Проверить foreign keys
fks = inspector.get_foreign_keys(sales)
for fk in fks:
    print(f"FK: {fk[constrained_columns]}{fk[referred_table]}.{fk[referred_columns]}")

Чеклист нормализации

  • Все ячейки содержат атомарные значения (1NF)
  • Non-key атрибуты зависят от всего primary key (2NF)
  • Non-key атрибуты не зависят друг от друга (3NF)
  • Нет транзитивных зависимостей (BCNF)
  • Избежаны аномалии вставки, обновления и удаления
  • Разумная денормализация для производительности

Заключение

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

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