Что такое вторая нормальная форма базы данных?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Вторая нормальная форма базы данных (2NF)
Нормализация базы данных — это фундаментальный concept, который я считаю must-know для любого Java Developer. Вторая нормальная форма (2NF) часто вызывает confusion, потому что её определение звучит complexity. За 10+ лет я видел how bad database design убивает приложения. Давайте разберёмся
Что такое 1NF (First Normal Form)?
Прежде чем говорить о 2NF, нужно понимать 1NF:
1NF требует:
- Все значения в ячейках должны быть atomic (неделимые)
- Нет повторяющихся группы данных
- Каждый столбец содержит values одного типа
Пример нарушения 1NF:
Таблица: Orders (НЕПРАВИЛЬНО)
┌──────────┬──────────────┐
│ order_id │ product_ids │
├──────────┼──────────────┤
│ 1 │ "1, 2, 5" │ ← Строка содержит список!
│ 2 │ "3" │
└──────────┴──────────────┘
Это нарушает 1NF, потому что product_ids не atomic.
Правильно (1NF):
Таблица: Orders
┌──────────┬──────────┐
│ order_id │ order_no │
├──────────┼──────────┤
│ 1 │ ORD-001 │
│ 2 │ ORD-002 │
└──────────┴──────────┘
Таблица: Order_Items
┌──────────┬──────────────┐
│ order_id │ product_id │
├──────────┼──────────────┤
│ 1 │ 1 │ ← Atomic!
│ 1 │ 2 │
│ 1 │ 5 │
│ 2 │ 3 │
└──────────┴──────────────┘
Что такое 2NF (Second Normal Form)?
2NF требует:
- Таблица должна быть в 1NF
- Все non-key атрибуты должны зависеть от PRIMARY KEY полностью (full functional dependency)
- Нет partial dependencies (зависимости от части составного ключа)
Проще: Если у вас composite primary key (из нескольких колонок), все остальные колонки должны зависеть от ВСЕГО ключа, не от его части.
Пример нарушения 2NF
Таблица: Student_Courses (НЕПРАВИЛЬНО - нарушает 2NF)
┌────────────┬──────────────┬─────────────┬──────────────┐
│ student_id │ course_id │ student_name│ instructor │
├────────────┼──────────────┼─────────────┼──────────────┤
│ 1 │ JAVA-101 │ John Smith │ Dr. Anderson │
│ 1 │ JAVA-101 │ John Smith │ Dr. Anderson │ ← Duplicate!
│ 1 │ PYTHON-201 │ John Smith │ Dr. Brown │
│ 2 │ JAVA-101 │ Jane Doe │ Dr. Anderson │
└────────────┴──────────────┴─────────────┴──────────────┘
Проблемы:
- Primary key: (student_id, course_id)
- student_name зависит ТОЛЬКО от student_id (не от course_id)
- instructor зависит ТОЛЬКО от course_id (не от student_id)
- Это partial dependencies!
Почему это плохо?
- Redundancy: John Smith повторяется в каждой его строке
- Update anomaly: Если John Smith переехал и изменился адрес, нужно обновить все строки
- Insert anomaly: Не можно добавить нового студента без курса
- Delete anomaly: Если удалить последний курс студента, удалится и студент
Как привести к 2NF
Разбиваем одну таблицу на три:
Таблица: Students (2NF)
┌────────────┬──────────────┐
│ student_id │ student_name │
├────────────┼──────────────┤
│ 1 │ John Smith │
│ 2 │ Jane Doe │
└────────────┴──────────────┘
Таблица: Courses (2NF)
┌────────────┬──────────────┐
│ course_id │ instructor │
├────────────┼──────────────┤
│ JAVA-101 │ Dr. Anderson │
│ PYTHON-201 │ Dr. Brown │
└────────────┴──────────────┘
Таблица: Student_Enrollments (2NF) - junction table
┌────────────┬────────────┐
│ student_id │ course_id │ ← Primary key: (student_id, course_id)
├────────────┼────────────┤
│ 1 │ JAVA-101 │
│ 1 │ PYTHON-201 │
│ 2 │ JAVA-101 │
└────────────┴────────────┘
Теперь:
- Каждый атрибут зависит от полного primary key
- Нет дублирования
- Updates, inserts, deletes работают без problems
Как это выглядит в JPA/Hibernate
// Сущность Student
@Entity
@Table(name = "students")
public class Student {
@Id
private Long studentId;
private String studentName;
@ManyToMany
@JoinTable(
name = "student_enrollments",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
}
// Сущность Course
@Entity
@Table(name = "courses")
public class Course {
@Id
private String courseId;
private String instructor;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
}
// Использование
Student student = new Student();
student.setStudentId(1L);
student.setStudentName("John Smith");
Course javaCourse = new Course();
javaCourse.setCourseId("JAVA-101");
javaCourse.setInstructor("Dr. Anderson");
student.getCourses().add(javaCourse);
studentRepository.save(student);
2NF vs 3NF vs BCNF
2NF: All non-key attributes depend on the whole key, not on part of it
(Removes partial dependencies)
3NF: In addition to 2NF, non-key attributes must depend DIRECTLY on key
(Removes transitive dependencies)
Example: student_id → student_name; student_name → advisor_id
This is transitive! 3NF требует это разделить
BCNF: Every determinant must be a candidate key
(Strictest form of normalization)
Реальный пример из моего опыта
Я работал над системой управления хранилищем товаров:
НЕЦ (нарушение 2NF):
Inventory_Bad:
┌──────────┬──────────────┬──────────┬─────────────┐
│warehouse_id│product_id │quantity │warehouse_city
├──────────┼──────────────┼──────────┼─────────────┤
│ W1 │ P001 │ 100 │ Moscow │
│ W1 │ P002 │ 50 │ Moscow │ ← Redundant!
│ W2 │ P001 │ 75 │ St.Petersburg│
└──────────┴──────────────┴──────────┴─────────────┘
Проблема: warehouse_city зависит ТОЛЬКО от warehouse_id
Правильно:
Warehouses (2NF):
┌──────────┬─────────────┐
│warehouse_id│warehouse_city
├──────────┼─────────────┤
│ W1 │ Moscow │
│ W2 │ St.Petersburg
└──────────┴─────────────┘
Inventory (2NF):
┌──────────┬──────────────┬──────────┐
│warehouse_id│product_id │quantity │
├──────────┼──────────────┼──────────┤
│ W1 │ P001 │ 100 │
│ W1 │ P002 │ 50 │
│ W2 │ P001 │ 75 │
└──────────┴──────────────┴──────────┘
Результат: Сняли 30% дублирования, тесты быстрее, обновления проще.
Практические советы
1. Всегда спрашивайте себя при составном ключе:
Есть ли столбцы, которые зависят только от ЧАСТИ ключа?
Если да — разделите таблицу.
2. Инструменты для анализа:
-- Посмотреть все foreign keys
SELECT constraint_name, table_name
FROM information_schema.table_constraints
WHERE constraint_type = 'PRIMARY KEY';
3. Не переусложняйте: 2NF достаточна для большинства приложений. 3NF для аналитики. BCNF редко нужна.
Вывод
2NF — это второй уровень нормализации, который удаляет partial dependencies. Если у вас composite primary key, убедитесь что ВСЕ остальные атрибуты зависят от ВСЕГО ключа, не от его части. Это prevent data anomalies и делает базу данных чище.