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

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

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

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

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

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

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

Нормализация — это процесс проектирования базы данных для минимизации избыточности данных и зависимостей, обеспечивая целостность и эффективность хранения информации.

Основная проблема (денормализованная БД)

-- ПЛОХО: не нормализованная таблица
CREATE TABLE orders (
  order_id INT PRIMARY KEY,
  customer_name VARCHAR(100),
  customer_email VARCHAR(100),
  customer_phone VARCHAR(20),
  customer_address VARCHAR(255),
  customer_country VARCHAR(50),
  customer_city VARCHAR(50),
  product_name VARCHAR(100),
  product_price DECIMAL(10, 2),
  product_category VARCHAR(50),
  product_manufacturer VARCHAR(100),
  quantity INT,
  total_amount DECIMAL(10, 2),
  order_date DATE
);

Проблемы:

  • Дублирование данных: имя и email клиента хранится для каждого заказа
  • Аномалия обновления: если имя клиента изменилось, нужно обновить все строки
  • Аномалия удаления: если удалить заказ, потеряются данные о клиенте
  • Аномалия вставки: нельзя добавить клиента без заказа
  • Растраты места: данные дублируются много раз

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

Нормализация разбивает одну большую таблицу на несколько связанных таблиц, следуя нормальным формам (Normal Forms).

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

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

-- ПЛОХО (не 1NF): массив в поле
CREATE TABLE orders (
  order_id INT PRIMARY KEY,
  customer_name VARCHAR(100),
  items VARCHAR(255)  -- '"laptop", "monitor", "mouse"' - список в строке!
);

-- ХОРОШО (1NF): каждое значение атомарно
CREATE TABLE orders (
  order_id INT PRIMARY KEY,
  customer_name VARCHAR(100)
);

CREATE TABLE order_items (
  item_id INT PRIMARY KEY,
  order_id INT REFERENCES orders(order_id),
  product_name VARCHAR(100)  -- одно значение
);

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

Правило: таблица должна быть в 1NF и каждое не-ключевое поле должно зависеть от всего первичного ключа (не от его части)

-- ПЛОХО (не 2NF): есть частичные зависимости
CREATE TABLE products_orders (
  order_id INT,
  product_id INT,
  product_name VARCHAR(100),      -- зависит только от product_id
  product_price DECIMAL(10, 2),   -- зависит только от product_id
  quantity INT,                   -- зависит от обоих
  PRIMARY KEY (order_id, product_id)
);

-- ХОРОШО (2NF): каждое поле зависит от полного ключа
CREATE TABLE order_items (
  order_id INT,
  product_id INT,
  quantity INT,
  PRIMARY KEY (order_id, product_id),
  FOREIGN KEY (order_id) REFERENCES orders(order_id),
  FOREIGN KEY (product_id) REFERENCES products(product_id)
);

CREATE TABLE products (
  product_id INT PRIMARY KEY,
  product_name VARCHAR(100),
  product_price DECIMAL(10, 2)
);

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

Правило: таблица должна быть в 2NF и не содержать транзитивных зависимостей (зависимости не-ключевых полей от других не-ключевых полей)

-- ПЛОХО (не 3NF): есть транзитивная зависимость
CREATE TABLE employees (
  employee_id INT PRIMARY KEY,
  employee_name VARCHAR(100),
  department_id INT,
  department_name VARCHAR(100),   -- зависит от department_id, а не от employee_id
  department_budget DECIMAL(12, 2) -- зависит от department_id, а не от employee_id
);

-- ХОРОШО (3NF): удалены транзитивные зависимости
CREATE TABLE employees (
  employee_id INT PRIMARY KEY,
  employee_name VARCHAR(100),
  department_id INT,
  FOREIGN KEY (department_id) REFERENCES departments(department_id)
);

CREATE TABLE departments (
  department_id INT PRIMARY KEY,
  department_name VARCHAR(100),
  department_budget DECIMAL(12, 2)
);

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

До нормализации (одна таблица):

CREATE TABLE orders (
  order_id, customer_name, customer_email, customer_phone,
  product_name, product_price, product_category,
  quantity, order_date
);

После нормализации (несколько таблиц):

CREATE TABLE customers (
  customer_id INT PRIMARY KEY,
  customer_name VARCHAR(100) NOT NULL,
  customer_email VARCHAR(100) NOT NULL,
  customer_phone VARCHAR(20),
  UNIQUE(customer_email)
);

CREATE TABLE products (
  product_id INT PRIMARY KEY,
  product_name VARCHAR(100) NOT NULL,
  product_price DECIMAL(10, 2) NOT NULL,
  product_category VARCHAR(50),
  UNIQUE(product_name)
);

CREATE TABLE orders (
  order_id INT PRIMARY KEY,
  customer_id INT NOT NULL,
  order_date DATE NOT NULL,
  total_amount DECIMAL(10, 2),
  FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

CREATE TABLE order_items (
  order_item_id INT PRIMARY KEY,
  order_id INT NOT NULL,
  product_id INT NOT NULL,
  quantity INT NOT NULL CHECK (quantity > 0),
  item_price DECIMAL(10, 2) NOT NULL,
  FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,
  FOREIGN KEY (product_id) REFERENCES products(product_id)
);

Преимущества нормализации

1. Минимизация дублирования

-- До: имя клиента хранится в каждой строке заказа
INSERT INTO orders (order_id, customer_name, ...) VALUES (1, 'John Doe', ...);
INSERT INTO orders (order_id, customer_name, ...) VALUES (2, 'John Doe', ...);
INSERT INTO orders (order_id, customer_name, ...) VALUES (3, 'John Doe', ...);
-- 3 раза одно и то же имя

-- После: имя хранится один раз
INSERT INTO customers (customer_id, customer_name) VALUES (1, 'John Doe');
INSERT INTO orders (order_id, customer_id) VALUES (1, 1);
INSERT INTO orders (order_id, customer_id) VALUES (2, 1);
INSERT INTO orders (order_id, customer_id) VALUES (3, 1);

2. Легче обновление

-- До: нужно обновить везде
UPDATE orders SET customer_name = 'Jane Doe' WHERE customer_name = 'John Doe';
-- Медленно и опасно

-- После: обновляем в одном месте
UPDATE customers SET customer_name = 'Jane Doe' WHERE customer_id = 1;
-- Быстро и безопасно

3. Целостность данных

-- Foreign key гарантирует что order.customer_id указывает на реального клиента
CREATE TABLE orders (
  order_id INT PRIMARY KEY,
  customer_id INT NOT NULL,
  FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

-- Нельзя создать заказ несуществующего клиента
INSERT INTO orders (order_id, customer_id) VALUES (1, 999); -- ERROR!

4. Экономия места на диске

Без нормализации:
1M заказов × customer_name (50 байт) = 50 MB (дублированные имена)

С нормализацией:
1M заказов × customer_id (4 байта) = 4 MB
100k клиентов × customer_name (50 байт) = 5 MB
Всего: 9 MB (в 5 раз меньше!)

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

1. Больше JOIN запросов

-- Нормализованная БД нужна JOIN
SELECT o.order_id, c.customer_name, p.product_name, oi.quantity
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.order_date > '2024-01-01';

-- Это медленнее чем выборка из одной таблицы

2. Сложнее запросы

-- Простой запрос превращается в сложный
SELECT * FROM orders WHERE customer_name = 'John';
-- становится
SELECT o.* 
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE c.customer_name = 'John';

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

Для OLAP систем (аналитика) часто используют денормализованные таблицы (Star Schema):

-- Fact table (факты)
CREATE TABLE sales_fact (
  sale_id INT PRIMARY KEY,
  customer_id INT,
  product_id INT,
  store_id INT,
  date_id INT,
  quantity INT,
  amount DECIMAL(10, 2)
);

-- Dimension tables (денормализованные)
CREATE TABLE customer_dim (
  customer_id INT PRIMARY KEY,
  customer_name VARCHAR(100),
  customer_email VARCHAR(100),
  country VARCHAR(50),
  city VARCHAR(50)
);

-- Это быстрее для аналитических запросов

Best Practices

1. Нормализуйте до 3NF

  • OLTP системы (транзакции, операционные системы)
  • Стандартный выбор

2. Денормализуйте если нужна скорость

  • OLAP системы (аналитика, BI)
  • High-performance требования

3. Используйте индексы вместо денормализации

-- Вместо денормализации
CREATE INDEX idx_orders_customer ON orders(customer_id);
CREATE INDEX idx_customers_name ON customers(customer_name);

4. Документируйте зависимости

-- Каждая таблица должна иметь clear PK и FK
-- Используй REFERENCES для явных связей

Резюме

Нормализация — это фундаментальный принцип проектирования БД, который:

  • Минимизирует дублирование
  • Упрощает обновление
  • Гарантирует целостность
  • Экономит место

Однако нужно балансировать между нормализацией и производительностью. Для OLTP используйте 3NF, для OLAP рассмотрите денормализацию.

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