Нормальная форма (Normal Form) — это набор правил и критериев, которым должна удовлетворять таблица в реляционной базе данных, чтобы обеспечить её логическую целостность, минимизировать избыточность и исключить аномалии при операциях с данными (добавлении, изменении, удалении).
Проще говоря, это “правила хорошего тона” для проектирования структуры ваших таблиц. Чем выше нормальная форма, тем более “правильно” и оптимально спроектирована ваша база данных.
Зачем это нужно?
Проектирование базы данных без соблюдения нормальных форм приводит к трем главным проблемам:
- Избыточность данных: Одни и те же данные повторяются в разных строках, занимая лишнее место.
- Аномалии обновления (Update Anomalies): Чтобы изменить какое-то значение, приходится обновлять множество строк. Если обновить не все, данные становятся противоречивыми.
- Аномалии удаления (Delete Anomalies): При удалении одной записи можно случайно потерять другую, связанную с ней информацию.
- Аномалии добавления (Insert Anomalies): Невозможно добавить одни данные, пока не добавлены другие.
Нормальные формы последовательно решают эти проблемы.
Основные нормальные формы (1NF, 2NF, 3NF, BCNF)
Существует несколько нормальных форм, но чаще всего используются первые три. Они идут по нарастающей: чтобы привести таблицу к 3NF, она сначала должна удовлетворять условиям 1NF и 2NF.
Рассмотрим их на простом и понятном примере.
Исходная “плохая” таблица Заказы_Книг:
| OrderID | Customer | CustomerPhone | Books | TotalPrice |
|---|---|---|---|---|
| 1 | Иванов И.И. | +79111111111 | ”Война и мир” (500р), “Преступление и наказание” (400р) | 900 |
| 2 | Петрова А.С. | +79222222222 | ”Мастер и Маргарита” (450р) | 450 |
| 3 | Иванов И.И. | +79111111111 | ”1984” (350р), “Мастер и Маргарита” (450р), “Война и мир” (500р) | 1300 |
1. Первая нормальная форма (1NF)
Правило: Все атрибуты (столбцы) таблицы должны быть атомарными (несоставными), а в таблице не должно быть повторяющихся групп.
Что это значит?
- В каждой ячейке должно быть только одно значение.
- Нельзя иметь столбцы, содержащие списки, множества или другие составные структуры.
Проблема в нашем примере: Столбец Books содержит списки книг — это нарушение 1NF.
Приводим к 1NF: Создаем отдельную строку для каждой книги в заказе.
| OrderID | Customer | CustomerPhone | BookTitle | Price |
|---|---|---|---|---|
| 1 | Иванов И.И. | +79111111111 | ”Война и мир” | 500 |
| 1 | Иванов И.И. | +79111111111 | ”Преступление и наказание” | 400 |
| 2 | Петрова А.С. | +79222222222 | ”Мастер и Маргарита” | 450 |
| 3 | Иванов И.И. | +79111111111 | ”1984” | 350 |
| 3 | Иванов И.И. | +79111111111 | ”Мастер и Маргарита” | 450 |
| 3 | Иванов И.И. | +79111111111 | ”Война и мир” | 500 |
Теперь в каждой ячейке — одно значение. 1NF достигнута. Но проблемы остались.
2. Вторая нормальная форма (2NF)
Правило: Таблица должна находиться в 1NF, и каждый неключевой атрибут должен полностью зависеть от всего первичного ключа.
Что это значит?
- Если первичный ключ составной (из нескольких столбцов), то любой другой столбец должен зависеть от всей этой комбинации, а не от её части.
Проблема в нашем примере (после 1NF):
- Первичный ключ, скорее всего, составной:
(OrderID, BookTitle). - Столбец
CustomerиCustomerPhoneзависят только отOrderID(части ключа), а не от всей связки(OrderID, BookTitle). Зная номер заказа, мы однозначно определяем клиента, независимо от того, какая это книга. - Это приводит к избыточности: данные об Иванове И.И. повторяются три раза.
Приводим к 2NF: Разделяем таблицу на две, вынося частичные зависимости.
Таблица Заказы (Orders):
| OrderID | Customer | CustomerPhone |
|---|---|---|
| 1 | Иванов И.И. | +79111111111 |
| 2 | Петрова А.С. | +79222222222 |
| 3 | Иванов И.И. | +79111111111 |
Таблица Позиции_Заказов (OrderItems):
| OrderID | BookTitle | Price |
|---|---|---|
| 1 | ”Война и мир” | 500 |
| 1 | ”Преступление и наказание” | 400 |
| 2 | ”Мастер и Маргарита” | 450 |
| 3 | ”1984” | 350 |
| 3 | ”Мастер и Маргарита” | 450 |
| 3 | ”Война и мир” | 500 |
Теперь неключевые атрибуты в каждой таблице полностью зависят от первичного ключа. 2NF достигнута. Избыточность данных клиента устранена.
3. Третья нормальная форма (3NF)
Правило: Таблица должна находиться в 2NF, и ни один неключевой атрибут не должен зависеть от другого неключевого атрибута (т.е. нет транзитивных зависимостей).
Что это значит?
- Все неключевые столбцы должны зависеть только от первичного ключа.
Проблема в нашем примере (после 2NF): В таблице Заказы столбец CustomerPhone зависит не от первичного ключа OrderID, а от другого неключевого атрибута — Customer. Это транзитивная зависимость: OrderID -> Customer -> CustomerPhone.
- Если Иванов сменит телефон, нам придется обновить все его заказы.
Приводим к 3NF: Выносим транзитивные зависимости в отдельную таблицу.
Таблица Клиенты (Customers):
| CustomerID | Customer | CustomerPhone |
|---|---|---|
| C1 | Иванов И.И. | +79111111111 |
| C2 | Петрова А.С. | +79222222222 |
Таблица Заказы (Orders):
| OrderID | CustomerID |
|---|---|
| 1 | C1 |
| 2 | C2 |
| 3 | C1 |
Таблица Позиции_Заказов (OrderItems): (остается без изменений)
| OrderID | BookTitle | Price |
|---|---|---|
| 1 | ”Война и мир” | 500 |
| 1 | ”Преступление и наказание” | 400 |
| … | … | … |
Теперь данные максимально защищены от аномалий. 3NF достигнута.
Краткий итог
- 1NF: “В каждой ячейке — одно значение, никаких списков”.
- 2NF: “Все данные о заказе — в таблице заказов, все о товаре в заказе — в таблице состава заказа. Никаких зависимостей от части ключа”.
- 3NF: “Все данные о клиенте — в таблице клиентов. В таблице заказов только ссылка на клиента. Никаких зависимостей ‘кто заказчик → его телефон’“.
Следование этим правилам приводит к созданию надежной, непротиворечивой и эффективной структуры базы данных. Однако в реальности иногда сознательно отступают от строгих нормальных форм (денормализация) для повышения производительности систем отчетности и аналитики (Data Warehouses).