← Назад к вопросам
Что такое вторая нормальная форма в БД?
2.0 Middle🔥 111 комментариев
#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Вторая нормальная форма (2NF) в базах данных
Вторая нормальная форма — это требование к проектированию реляционных баз данных. Она базируется на понятии функциональной зависимости и полностью решает проблемы аномалий частичной зависимости.
1. Определение 2NF
Таблица находится во второй нормальной форме, если:
- Она находится в первой нормальной форме (1NF)
- Все неключевые атрибуты полностью функционально зависимы от первичного ключа
Полная функциональная зависимость означает:
- Атрибут зависит от всего первичного ключа, а не от части ключа
# Пример: Таблица Orders (не в 2NF)
Orders = {
"order_id": 1, # Первичный ключ (часть)
"product_id": 100, # Первичный ключ (часть)
"quantity": 5, # Зависит от ВСЕГО (order_id, product_id) ✓
"product_name": "Laptop", # Зависит ТОЛЬКО от product_id ✗
"product_price": 999.99 # Зависит ТОЛЬКО от product_id ✗
}
# Проблема: product_name и product_price зависят только от product_id,
# а не от составного ключа (order_id, product_id)
2. Пример нарушения 2NF
# ❌ Плохо: нарушает 2NF
# Таблица: StudentCourses
StudentCourses = [
{
"student_id": 1, # Первичный ключ (часть)
"course_id": 101, # Первичный ключ (часть)
"grade": "A", # Полностью зависит от (student_id, course_id) ✓
"professor_name": "Dr. Smith", # Зависит ТОЛЬКО от course_id ✗
"professor_office": "Room 301" # Зависит ТОЛЬКО от course_id ✗
}
]
# Проблема:
# professor_name и professor_office зависят только от course_id,
# а не от ВСЕГО первичного ключа (student_id, course_id)
# Результаты:
# - Если удалить студента, потеряются данные о профессоре
# - Если изменить профессора для одного студента, нужно менять для всех
# - Если добавить курс без студентов, нельзя сохранить данные профессора
3. Приведение к 2NF
Решение: разложить таблицу на несколько таблиц.
# ✅ Хорошо: приведено к 2NF
# Таблица 1: StudentCourses (содержит ТОЛЬКО зависимости от всего ключа)
StudentCourses = [
{
"student_id": 1,
"course_id": 101,
"grade": "A" # Зависит от ВСЕГО ключа
}
]
# Таблица 2: Courses (содержит зависимости от course_id)
Courses = [
{
"course_id": 101,
"course_name": "Python Programming",
"professor_id": 1 # Внешний ключ
}
]
# Таблица 3: Professors (содержит зависимости от professor_id)
Professors = [
{
"professor_id": 1,
"professor_name": "Dr. Smith",
"professor_office": "Room 301"
}
]
4. SQL примеры
-- ❌ Плохо: нарушает 2NF
CREATE TABLE Orders (
order_id INT,
product_id INT,
quantity INT,
product_name VARCHAR(100), -- Зависит только от product_id
product_price DECIMAL(10,2), -- Зависит только от product_id
PRIMARY KEY (order_id, product_id)
);
-- ✅ Хорошо: приведено к 2NF
CREATE TABLE Orders (
order_id INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, product_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)
);
5. Частичная зависимость vs Полная зависимость
# Первичный ключ: (student_id, semester)
# ✓ Полная зависимость (все атрибуты в 2NF)
# - grade зависит от (student_id, semester) целиком
# - attendance зависит от (student_id, semester) целиком
# - Нельзя узнать grade зная только student_id или только semester
# ✗ Частичная зависимость (нарушение 2NF)
# - student_name зависит только от student_id (часть ключа)
# - student_email зависит только от student_id (часть ключа)
# - semester_start_date зависит только от semester (часть ключа)
# - semester_end_date зависит только от semester (часть ключа)
# Решение: создать отдельные таблицы
# Students (student_id, student_name, student_email)
# Semesters (semester, semester_start_date, semester_end_date)
# Enrollments (student_id, semester, grade, attendance)
6. Python пример: проверка 2NF
from typing import List, Dict, Set, Tuple
def check_2nf(table_name: str, data: List[Dict],
primary_key: List[str],
non_key_attributes: List[str]) -> bool:
"""
Проверяет, находится ли таблица во второй нормальной форме.
2NF нарушается если:
- Неключевой атрибут зависит только от части первичного ключа
"""
# Для каждого подмножества первичного ключа
for key_part_size in range(1, len(primary_key)):
key_subsets = get_all_subsets(primary_key, key_part_size)
for key_subset in key_subsets:
# Проверяем, зависит ли какой-то неключевой атрибут ТОЛЬКО от этого подмножества
for record in data:
key_values = {k: record[k] for k in key_subset}
for attr in non_key_attributes:
# Если два записи имеют одинаковые значения части ключа
# и разные значения атрибута, то нарушение 2NF
matching_records = [
r for r in data
if all(r[k] == key_values[k] for k in key_subset)
]
attr_values = set(r[attr] for r in matching_records)
if len(attr_values) > 1:
# Атрибут зависит от всего ключа, не от части
continue
return False
return True
def get_all_subsets(elements: List, size: int) -> List[Tuple]:
"""Получить все подмножества размера size"""
from itertools import combinations
return list(combinations(elements, size))
# Использование
students_courses = [
{"student_id": 1, "course_id": 101, "grade": "A", "professor": "Dr. Smith"},
{"student_id": 2, "course_id": 101, "grade": "B", "professor": "Dr. Smith"},
]
is_2nf = check_2nf(
table_name="StudentCourses",
data=students_courses,
primary_key=["student_id", "course_id"],
non_key_attributes=["grade", "professor"]
)
print(f"Is in 2NF: {is_2nf}") # False, потому что professor зависит только от course_id
7. Практическое влияние
| Проблема | Причина | Решение |
|---|---|---|
| Аномалия обновления | Один атрибут хранится несколько раз | Нормализация (разложить таблицу) |
| Аномалия удаления | Удаление одной записи потеряет другие данные | Нормализация |
| Аномалия вставки | Нельзя вставить данные без всего ключа | Нормализация |
| Избыточность | Повторяющиеся данные занимают место | Нормализация |
Соотношение нормальных форм
1NF ← базовое требование
↓
2NF ← нет частичных зависимостей
↓
3NF ← нет транзитивных зависимостей
↓
BCNF ← все функциональные зависимости от ключей
↓
4NF ← разделены многозначные зависимости
↓
5NF ← разложение без потерь
Каждая форма более строгая, чем предыдущая.
Выводы
- 2NF требует: каждый неключевой атрибут зависит от ВСЕГО первичного ключа
- Решение: разложить таблицы по функциональным зависимостям
- Преимущества: меньше аномалий обновления, меньше избыточности
- Применение: практически все modern БД используют минимум 3NF