Что такое третья нормальная форма базы данных?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое третья нормальная форма базы данных (3NF)
Третья нормальная форма (3NF) — это уровень нормализации схемы базы данных, при котором устраняются аномалии обновления, удаления и вставки, вызванные функциональными зависимостями от неключевых атрибутов. Это один из стандартов для проектирования хорошей схемы БД.
История нормализации
Нормализация — это процесс организации данных в базе данных для:
- Минимизации избыточности (дублирования) данных
- Улучшения целостности данных
- Облегчения обслуживания
- Повышения производительности
Уровни нормализации от худшего к лучшему:
- Ненормализованная форма (UNF) — полное дублирование
- Первая нормальная форма (1NF) — атомарные значения
- Вторая нормальная форма (2NF) — нет частичных зависимостей
- Третья нормальная форма (3NF) — нет транзитивных зависимостей
- НФБК (BCNF) — более строгий вариант 3NF
Принципы третьей нормальной формы
Для соответствия 3NF таблица должна:
- Находиться во 2NF (первое требование)
- Не иметь транзитивных зависимостей — неключевой атрибут не должен зависеть от другого неключевого атрибута
Пример проблемы: БЕЗ 3NF
-- ПЛОХО: Не соответствует 3NF
CREATE TABLE Employees (
employee_id INT PRIMARY KEY,
name VARCHAR(100),
department_id INT,
department_name VARCHAR(100), -- Зависит от department_id, не от employee_id!
department_location VARCHAR(100) -- Также зависит от department_id
);
Проблемы:
-
Аномалия обновления: если изменить имя отдела, нужно обновить все строки сотрудников
-- Нужно обновить 50 строк! UPDATE Employees SET department_name = 'Engineering' WHERE department_id = 1; -
Аномалия удаления: удаление последнего сотрудника отдела теряет информацию об отделе
-- Удаляем сотрудника — и теряем информацию об отделе! DELETE FROM Employees WHERE employee_id = 999; -
Аномалия вставки: нельзя добавить новый отдел без сотрудника
-- Нельзя вставить новый отдел! -- employee_id PRIMARY KEY не может быть NULL INSERT INTO Employees (name, department_id, department_name) VALUES (NULL, 5, 'Marketing');
Решение: приведение к 3NF
-- ХОРОШО: Соответствует 3NF
CREATE TABLE Departments (
department_id INT PRIMARY KEY,
department_name VARCHAR(100),
location VARCHAR(100)
);
CREATE TABLE Employees (
employee_id INT PRIMARY KEY,
name VARCHAR(100),
department_id INT,
FOREIGN KEY (department_id) REFERENCES Departments(department_id)
);
Преимущества:
- Одна таблица для данных отдела
- Обновление отдела происходит в одном месте
- Можно добавить отдел без сотрудников
- Удаление сотрудника не теряет информацию об отделе
Более сложный пример
-- ПЛОХО: Нарушает 3NF
CREATE TABLE StudentCourses (
student_id INT,
course_id INT,
professor_id INT,
professor_name VARCHAR(100), -- Зависит от professor_id, не от ключа!
professor_email VARCHAR(100), -- Зависит от professor_id
course_name VARCHAR(100),
credits INT,
PRIMARY KEY (student_id, course_id)
);
Повторяющиеся данные профессора и курса для каждого студента!
-- ХОРОШО: Соответствует 3NF
CREATE TABLE Professors (
professor_id INT PRIMARY KEY,
professor_name VARCHAR(100),
professor_email VARCHAR(100)
);
CREATE TABLE Courses (
course_id INT PRIMARY KEY,
course_name VARCHAR(100),
credits INT,
professor_id INT,
FOREIGN KEY (professor_id) REFERENCES Professors(professor_id)
);
CREATE TABLE StudentCourses (
student_id INT,
course_id INT,
grade CHAR(1),
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (course_id) REFERENCES Courses(course_id)
);
Практический пример: E-commerce
-- ПЛОХО: Без нормализации
CREATE TABLE Orders (
order_id INT PRIMARY KEY,
customer_id INT,
customer_name VARCHAR(100),
customer_email VARCHAR(100),
customer_phone VARCHAR(20),
customer_city VARCHAR(100),
customer_country VARCHAR(100),
product_id INT,
product_name VARCHAR(100),
product_price DECIMAL(10,2),
product_category VARCHAR(50),
supplier_name VARCHAR(100),
supplier_phone VARCHAR(20),
order_date DATE,
quantity INT,
total_amount DECIMAL(10,2)
);
Проблемы:
- Данные клиента повторяются для каждого заказа
- Данные продукта повторяются для каждого заказа
- Данные поставщика повторяются для каждого продукта
-- ХОРОШО: Нормализовано до 3NF
CREATE TABLE Customers (
customer_id INT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100),
phone VARCHAR(20),
city VARCHAR(100),
country VARCHAR(100)
);
CREATE TABLE Suppliers (
supplier_id INT PRIMARY KEY,
name VARCHAR(100),
phone VARCHAR(20),
email VARCHAR(100),
country VARCHAR(100)
);
CREATE TABLE ProductCategories (
category_id INT PRIMARY KEY,
category_name VARCHAR(50)
);
CREATE TABLE Products (
product_id INT PRIMARY KEY,
name VARCHAR(100),
price DECIMAL(10,2),
category_id INT,
supplier_id INT,
FOREIGN KEY (category_id) REFERENCES ProductCategories(category_id),
FOREIGN KEY (supplier_id) REFERENCES Suppliers(supplier_id)
);
CREATE TABLE Orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATE,
FOREIGN KEY (customer_id) REFERENCES Customers(customer_id)
);
CREATE TABLE OrderItems (
order_item_id INT PRIMARY KEY,
order_id INT,
product_id INT,
quantity INT,
unit_price DECIMAL(10,2),
FOREIGN KEY (order_id) REFERENCES Orders(order_id),
FOREIGN KEY (product_id) REFERENCES Products(product_id)
);
Проверка 3NF
У каждой неключевой колонки должна быть зависимость только от полного ключа и ничего больше:
# Проверка 3NF
def check_3nf(table):
for column in table.non_key_columns:
# Колонка должна зависеть ОТ всего ключа
depends_on = get_dependencies(column)
# И НЕ должна зависеть от других неключевых колонок
for other in table.non_key_columns:
if other != column and depends_on(other):
return False # Нарушение 3NF!
return True
3NF vs BCNF
3NF позволяет:
- Некоторые аномалии, связанные с кандидатными ключами
- Зависимость неключевого атрибута от кандидатного ключа
BCNF (Boyce-Codd Normal Form):
- Более строгий стандарт
- Не допускает никаких аномалий
- Требует, чтобы каждый детерминант был кандидатным ключом
-- Пример, где 3NF но не BCNF
CREATE TABLE StudentLecturers (
student_id INT,
subject VARCHAR(50),
lecturer_id INT,
-- Уникальный индекс: только один лектор на предмет и студента
UNIQUE(student_id, subject),
UNIQUE(subject, lecturer_id)
);
-- Проблема BCNF: lecturer_id → subject
-- Но lecturer_id не является ключом таблицы
-- Это допускается в 3NF, но не в BCNF
Практические рекомендации
# ✓ ХОРОШО: Большинство таблиц должны быть в 3NF
class Database:
# Разделить данные по смыслу
users = {
'id': 'PK',
'name': 'depends on id only',
'email': 'depends on id only'
}
departments = {
'id': 'PK',
'name': 'depends on id only'
}
employees = {
'id': 'PK',
'name': 'depends on id only',
'department_id': 'FK'
}
# ✗ ПЛОХО: Смешивание данных
class BadDatabase:
everything = {
'employee_id': 'PK',
'employee_name': 'depends on employee_id',
'department_name': 'depends on department_id (транзитивная зависимость!)',
'location': 'depends on department_name'
}
Производительность
-- Нормализованная БД требует JOIN'ов
SELECT e.name, d.name, d.location
FROM Employees e
JOIN Departments d ON e.department_id = d.department_id;
-- Денормализованная БД быстрее для чтения, но хуже для обновления
SELECT name, department_name, department_location
FROM Employees;
Баланс между нормализацией и производительностью
-- В большинстве случаев 3NF оптимален
-- Если нужна производительность, можно использовать денормализацию
-- с индексами, кэшированием или материализованными представлениями
CREATE MATERIALIZED VIEW EmployeeDepartmentView AS
SELECT e.id, e.name, d.name as dept_name, d.location
FROM Employees e
JOIN Departments d ON e.department_id = d.department_id;
Заключение
Третья нормальная форма — это практический стандарт для проектирования реляционных базы данных. Она:
✓ Устраняет большинство аномалий ✓ Минимизирует дублирование ✓ Облегчает обслуживание ✓ Оптимален для большинства приложений ✓ Баланс между нормализацией и производительностью
Получение наборе данных в 3NF — ключевой навык для разработчика баз данных.