Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие знаю формы баз данных:
Нормальные формы (Normal Forms) в SQL
Нормальные формы нужны чтобы избежать аномалий при обновлении данных и избыточности. Существует 5+ форм, но на практике используют первые три.
0-я нормальная форма (Unnormalized Data)
Проблема: Данные хранятся "как есть" без структуры.
Таблица Students:
|ID | Name | Courses |
|----|---------|--------------------------------|
|1 | Alice | Math, Physics, Chemistry |
|2 | Bob | English, History |
Проблемы:
- Нельзя искать всех, кто учит Math (нужен LIKE)
- Нельзя удалить курс без обновления всех строк
- Избыточность данных
1NF (First Normal Form) — Атомарные значения
Правило: Каждое поле должно содержать только атомарное (неделимое) значение.
Решение: Создаем отдельную таблицу для many-to-many.
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE courses (
id INT PRIMARY KEY,
course_name VARCHAR(100)
);
CREATE TABLE enrollments (
student_id INT REFERENCES students(id),
course_id INT REFERENCES courses(id),
PRIMARY KEY (student_id, course_id)
);
Теперь можно запросить всех студентов на Physics:
SELECT s.name FROM students s
JOIN enrollments e ON s.id = e.student_id
JOIN courses c ON e.course_id = c.id
WHERE c.course_name = 'Physics';
2NF (Second Normal Form) — Зависимость от PRIMARY KEY
Правило: Все атрибуты должны зависеть от ВСЕГО первичного ключа, не от его части.
Проблема (до 2NF):
CREATE TABLE enrollments (
student_id INT,
course_id INT,
student_name VARCHAR(100), -- Зависит только от student_id!
course_name VARCHAR(100), -- Зависит только от course_id!
grade CHAR(1), -- Зависит от обоих (правильно)
PRIMARY KEY (student_id, course_id)
);
Проблемы:
- Если студент сменил имя, нужно обновлять несколько строк
- Можно добавить студента без курса? Нет!
- Аномалия удаления: удалим последний курс студента — потеряем его имя
Решение (2NF):
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(100) -- Зависит только от id
);
CREATE TABLE courses (
id INT PRIMARY KEY,
name VARCHAR(100) -- Зависит только от id
);
CREATE TABLE enrollments (
student_id INT REFERENCES students(id),
course_id INT REFERENCES courses(id),
grade CHAR(1), -- Зависит от обоих ключей
PRIMARY KEY (student_id, course_id)
);
3NF (Third Normal Form) — Транзитивная зависимость
Правило: Атрибуты не должны зависеть от других атрибутов (только от PRIMARY KEY).
Проблема (до 3NF):
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(100),
department_id INT,
department_name VARCHAR(100), -- Зависит от department_id, не от id!
manager_name VARCHAR(100) -- Зависит от department_id, не от id!
);
Проблемы:
- Если переименуем отдел, нужно обновить все сотрудники
- Можно добавить сотрудника с department_id, которого нет?
Решение (3NF):
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(100),
department_id INT REFERENCES departments(id)
);
CREATE TABLE departments (
id INT PRIMARY KEY,
name VARCHAR(100),
manager_id INT REFERENCES employees(id)
);
Теперь каждый атрибут зависит ТОЛЬКО от PRIMARY KEY.
BCNF (Boyce-Codd Normal Form) — Строгая 3NF
Правило: Каждый детерминант должен быть candidate key.
Пример (редко нужна на практике):
-- Профессоры учат курсы, каждая комбинация professor-course уникальна
CREATE TABLE professor_course (
professor_id INT,
course_id INT,
time VARCHAR(20),
PRIMARY KEY (professor_id, course_id, time)
);
Если professor_id → time (профессор всегда в одно время), это нарушает BCNF. Решение: отдельная таблица.
4NF / 5NF — Мультизначные и связанные зависимости
Эти формы очень редки на практике. Используются только в сложных аналитических системах.
Практические рекомендации
Когда денормализовать?
Не всегда нужно максимальная нормализация. Денормализация может помочь:
- Кэширование часто запрашиваемых данных:
-- Денормализованный подсчет (кэш)
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100),
post_count INT DEFAULT 0 -- Кэш количества постов
);
-- При добавлении поста обновляем кэш:
UPDATE users SET post_count = post_count + 1 WHERE id = ?;
- Денормализованные отчеты:
-- 3NF таблицы для продаж
SELECT customer.name, product.price, order.quantity
FROM orders
JOIN customers ON orders.customer_id = customers.id
JOIN products ON orders.product_id = products.id;
-- Денормализованная таблица для аналитики (материализованное представление)
CREATE MATERIALIZED VIEW sales_report AS
SELECT customer_name, product_name, price * quantity as total
FROM ...
Таблица сравнения
| Форма | Правило | Плюсы | Минусы |
|---|---|---|---|
| 1NF | Атомарные значения | Убирает повторения | Может потребоваться больше таблиц |
| 2NF | От полного PRIMARY KEY | Аномалии на одну таблицу меньше | Больше JOIN'ов |
| 3NF | Нет транзитивных зависимостей | Чистая структура | Сложно запрашивать |
| BCNF | Все детерминанты = candidate keys | Максимально чисто | На практике редко нужна |
Примеры в Node.js + TypeORM
// 3NF: students и courses в отдельных таблицах
@Entity()
export class Student {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(() => Course, course => course.students)
@JoinTable()
courses: Course[];
}
@Entity()
export class Course {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(() => Student, student => student.courses)
students: Student[];
}
Главное: Нормализуй до 3NF по умолчанию, денормализуй только если есть реальные проблемы производительности.