Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие знаешь уровни нормализации?
Нормализация — это процесс структурирования данных в БД для устранения дублирования и обеспечения целостности. Это фундаментальный концепт проектирования реляционных БД.
Проблема без нормализации
-- Денормализованная таблица (ПЛОХО)
CREATE TABLE users_denormalized (
id INT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100),
phone VARCHAR(20),
address VARCHAR(255),
city VARCHAR(50),
country VARCHAR(50),
department_name VARCHAR(100), -- дублируется если 100 юзеров в отделе!
manager_name VARCHAR(100),
manager_email VARCHAR(100)
);
-- Проблемы:
-- 1. Дублирование данных (department_name повторяется)
-- 2. Аномалии обновления (поменять department → 100 UPDATE)
-- 3. Аномалии удаления (удалить юзера → потеряется department)
-- 4. Много NULL значений
-- 5. Неэффективное использование памяти
Нулевая нормальная форма (Unnormalized)
Данные просто собраны в одну таблицу без структуры.
-- Пример: JSON вместо отдельной таблицы
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_name VARCHAR(100),
items JSON, -- вложенные данные
total DECIMAL(10,2)
);
INSERT INTO orders VALUES (
1,
'John Doe',
'[{"product": "Laptop", "price": 1000}, {"product": "Mouse", "price": 50}]',
1050
);
-- Проблема: сложно искать по items
Первая нормальная форма (1NF)
Правило: все значения должны быть атомарными (неделимыми).
-- ПЛОХО - нарушение 1NF
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(100),
courses VARCHAR(255) -- "Math,Physics,Chemistry" - не атомарно!
);
-- ХОРОШО - соответствие 1NF
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE student_courses (
student_id INT REFERENCES students(id),
course_name VARCHAR(100),
PRIMARY KEY (student_id, course_name)
);
-- Теперь каждое значение атомарно
INSERT INTO student_courses VALUES (1, 'Math');
INSERT INTO student_courses VALUES (1, 'Physics');
INSERT INTO student_courses VALUES (1, 'Chemistry');
Проверка 1NF:
- Нет повторяющихся групп
- Нет массивов или JSON вложенности
- Каждое поле содержит одно значение
Вторая нормальная форма (2NF)
Правило: удовлетворять 1NF + все неключевые атрибуты должны зависеть от всего первичного ключа, не от его части.
-- ПЛОХО - нарушение 2NF
CREATE TABLE student_courses (
student_id INT,
course_id INT,
course_name VARCHAR(100), -- зависит только от course_id, не от student_id!
professor_name VARCHAR(100), -- зависит только от course_id!
grade CHAR(1), -- зависит от (student_id, course_id) - ХОРОШО
PRIMARY KEY (student_id, course_id)
);
-- Проблемы:
-- - Если изменить professor, нужно UPDATE всех студентов курса
-- - Если удалить последнего студента, потеряем информацию о курсе
-- ХОРОШО - соответствие 2NF
CREATE TABLE courses (
id INT PRIMARY KEY,
name VARCHAR(100),
professor_id INT REFERENCES professors(id)
);
CREATE TABLE student_courses (
student_id INT REFERENCES students(id),
course_id INT REFERENCES courses(id),
grade CHAR(1),
PRIMARY KEY (student_id, course_id)
);
CREATE TABLE professors (
id INT PRIMARY KEY,
name VARCHAR(100)
);
-- Теперь:
-- - Каждое неключевое поле зависит от ВСЕГО первичного ключа
-- - Обновление professor = 1 UPDATE в таблице professors
Проверка 2NF:
- Удовлетворять 1NF
- Нет частичных зависимостей от первичного ключа
- Все неключевые поля зависят от полного ключа
Третья нормальная форма (3NF)
Правило: удовлетворять 2NF + неключевые атрибуты не должны зависеть друг от друга (транзитивная зависимость).
-- ПЛОХО - нарушение 3NF
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(100),
department_id INT,
department_name VARCHAR(100), -- зависит от department_id (транзитив!)
manager_id INT,
manager_name VARCHAR(100) -- зависит от manager_id (транзитив!)
);
-- Проблемы:
-- - Если изменить department_name → UPDATE все записи
-- - Аномалии удаления и обновления
-- ХОРОШО - соответствие 3NF
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(100),
department_id INT REFERENCES departments(id),
manager_id INT REFERENCES employees(id)
);
CREATE TABLE departments (
id INT PRIMARY KEY,
name VARCHAR(100),
location VARCHAR(100)
);
-- Теперь неключевые поля не зависят друг от друга
Проверка 3NF:
- Удовлетворять 2NF
- Нет транзитивных зависимостей
- Неключевые поля зависят только от первичного ключа
Нормальная форма Бойса-Кодда (BCNF)
Правило: более строгая версия 3NF. Каждый детерминант (поле, от которого зависит другое) должен быть кандидатом в первичные ключи.
-- ПЛОХО - в 3NF, но не в BCNF
CREATE TABLE professor_courses (
professor_id INT,
course_id INT,
time_slot INT,
classroom INT,
PRIMARY KEY (professor_id, course_id)
);
-- Правило: один professor может вести один course
-- Но classroom зависит от time_slot, не от (professor, course)!
-- Если несколько courses в одном time_slot → конфликт
-- ХОРОШО - соответствие BCNF
CREATE TABLE professor_courses (
professor_id INT REFERENCES professors(id),
course_id INT REFERENCES courses(id),
PRIMARY KEY (professor_id, course_id)
);
CREATE TABLE course_schedule (
course_id INT REFERENCES courses(id),
time_slot INT,
classroom INT,
PRIMARY KEY (course_id, time_slot)
);
-- Теперь каждый детерминант - кандидат в первичные ключи
4NF - Четвёртая нормальная форма
Правило: BCNF + отсутствие нетривиальных многозначных зависимостей.
Редко нужна в practice.
-- ПЛОХО
CREATE TABLE student_hobbies_languages (
student_id INT,
hobby VARCHAR(50),
language VARCHAR(50),
PRIMARY KEY (student_id, hobby, language)
);
-- Если Иван говорит на English и French
-- И играет в Football и Basketball
-- Нужно хранить 4 комбинации (2x2) вместо 4
-- ХОРОШО - разделить независимые многозначные свойства
CREATE TABLE student_hobbies (
student_id INT REFERENCES students(id),
hobby VARCHAR(50),
PRIMARY KEY (student_id, hobby)
);
CREATE TABLE student_languages (
student_id INT REFERENCES students(id),
language VARCHAR(50),
PRIMARY KEY (student_id, language)
);
Практический пример: Интернет-магазин
Денормализованная (ПЛОХО):
CREATE TABLE orders (
id INT,
customer_name VARCHAR(100),
customer_email VARCHAR(100),
product_name VARCHAR(100),
product_price DECIMAL(10,2),
quantity INT,
total DECIMAL(10,2),
status VARCHAR(50)
);
3NF нормализованная (ХОРОШО):
CREATE TABLE customers (
id INT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100) UNIQUE
);
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100),
price DECIMAL(10,2)
);
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT REFERENCES customers(id),
status VARCHAR(50),
created_at TIMESTAMP
);
CREATE TABLE order_items (
id INT PRIMARY KEY,
order_id INT REFERENCES orders(id),
product_id INT REFERENCES products(id),
quantity INT,
unit_price DECIMAL(10,2)
);
Когда нарушать нормализацию (денормализация)
Иногда нормализация медленнее. Тогда денормализуют:
-- Часто нужна total сумма заказа
-- Вместо каждый раз считать:
-- SELECT SUM(quantity * unit_price) FROM order_items WHERE order_id = 1
-- Добавляем денормализованное поле:
ALTER TABLE orders ADD COLUMN total DECIMAL(10,2);
-- Обновляем через триггер:
CREATE TRIGGER update_order_total
AFTER INSERT ON order_items
FOR EACH ROW
BEGIN
UPDATE orders
SET total = (SELECT SUM(quantity * unit_price) FROM order_items WHERE order_id = NEW.order_id)
WHERE id = NEW.order_id;
END;
Но это усложняет поддержку!
Таблица уровней
Уровень Требование
──────────┼────────────────────────────────────────────────
Unnorm. Данные в одной таблице
1NF Атомарные значения, нет повторяющихся групп
2NF Нет частичных зависимостей от ключа
3NF Нет транзитивных зависимостей
BCNF Строже 3NF, детерминанты = ключи
4NF Нет многозначных зависимостей
5NF Нет join зависимостей (очень редко)
Мой подход в production
1. Начинаю с 3NF (золотой стандарт)
2. Профилирую запросы
3. Если медленно → добавляю индексы
4. Если всё ещё медленно → денормализую (с триггерами или кэшем)
5. Храню это в документации
// В коде (ORM)
const order = await db.query(`
SELECT
o.id, o.created_at,
c.name, c.email,
SUM(oi.quantity * oi.unit_price) as total,
COUNT(oi.id) as item_count
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN order_items oi ON o.id = oi.order_id
WHERE o.id = $1
GROUP BY o.id, c.id
`);
Вывод: 3NF — стандарт modern databases. Понимание нормализации критично для дизайна БД, но в practice комбинируют нормализацию с денормализацией для производительности.