Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое нормализация БД
Нормализация — это процесс проектирования базы данных для минимизации избыточности данных и зависимостей, обеспечивая целостность и эффективность хранения информации.
Основная проблема (денормализованная БД)
-- ПЛОХО: не нормализованная таблица
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 рассмотрите денормализацию.