Что такое третья нормальная форма (3NF) в базах данных?

Ответ

Третья нормальная форма (3NF) — это правило проектирования реляционных баз данных, направленное на устранение избыточности данных и аномалий при обновлении. Таблица находится в 3NF, если:

  1. Она находится во второй нормальной форме (2NF) (все неключевые атрибуты полностью зависят от первичного ключа).
  2. Отсутствуют транзитивные зависимости: ни один неключевой атрибут не зависит от другого неключевого атрибута. Каждый неключевой атрибут должен зависеть только от первичного ключа.
Пример нарушения 3NF: Допустим, у нас есть таблица Заказы (Orders): OrderID (PK) CustomerID CustomerName CustomerEmail
1 101 Иван Петров ivan@example.com
2 101 Иван Петров ivan@example.com
3 102 Мария Сидорова maria@example.com

Здесь CustomerName и CustomerEmail зависят не от OrderID, а от CustomerID. Это транзитивная зависимость. При смене email клиента пришлось бы обновлять все его заказы, что ведет к аномалии обновления и избыточности.

Приведение к 3NF: Нужно разделить таблицу, вынеся информацию о клиенте в отдельную сущность.

Таблица Заказы (Orders): OrderID (PK) CustomerID (FK)
1 101
2 101
3 102
Таблица Клиенты (Customers): CustomerID (PK) CustomerName CustomerEmail
101 Иван Петров ivan@example.com
102 Мария Сидорова maria@example.com

Теперь данные о клиенте хранятся в одном месте. В контексте Node.js при работе, например, с ORM Sequelize, это будет отражено в двух связанных моделях с отношением hasMany / belongsTo.

Ответ 18+ 🔞

А, слушай, про третью нормальную форму! Ну это ж классика, ёпта. Сидишь такой, проектируешь базу, думаешь — ну чё, вроде табличка, поля есть, вроде норм. А потом бац — и начинаются аномалии, как в плохом триллере. Данные дублируются, обновлять их — пиздец, терпения ноль ебать.

Так вот, чтобы не было этой мути, умные дядьки придумали 3NF. Суть проще пареной репы, если не усложнять. Таблица в третьей нормальной форме, если:

  1. Она уже во второй (2NF) — то есть все неключевые поля зависят от всего первичного ключа, а не от его кусочка. Это база.
  2. И тут главное: нет транзитивных зависимостей. Это когда какой-то неключевой атрибут зависит не от ключа напрямую, а от другого такого же неключевого поля. Получается цепочка: ключ → поле А → поле Б. Вот это — пизда рулю. От этого и надо избавляться.

Смотри, как это бывает на практике:

Допустим, делаем таблицу Заказы. Накосячили, как обычно в первом приближении.

Orders
| OrderID (PK) | CustomerID | CustomerName | CustomerEmail       |
|--------------|------------|--------------|---------------------|
| 1            | 101        | Иван Петров  | ivan@example.com    |
| 2            | 101        | Иван Петров  | ivan@example.com    |
| 3            | 102        | Мария Сидорова | maria@example.com |

Смотри, в чём подвох. OrderID — ключ. А имя клиента и его почта нихуя от номера заказа не зависят. Они привязаны к CustomerID. Получается, CustomerName зависит от CustomerID, а тот уже от OrderID. Это и есть та самая транзитивная зависимость, хитрая жопа.

И что в итоге? Если Иван Петров сменит почту, тебе придётся бегать и латать её в каждой его строчке заказа. Овердохуища работы, и если забудешь одну — данные поехали, доверия ебать ноль. Плюс дублирование — почта Ивана хранится в двух местах. Зачем? Манда с ушами получается.

Как лечить? Да ебушки-воробушки, всё гениальное просто — режем таблицу на две.

Таблица Заказы (Orders):

| OrderID (PK) | CustomerID (FK) |
|--------------|-----------------|
| 1            | 101             |
| 2            | 101             |
| 3            | 102             |

Таблица Клиенты (Customers):

| CustomerID (PK) | CustomerName | CustomerEmail       |
|-----------------|--------------|---------------------|
| 101             | Иван Петров  | ivan@example.com    |
| 102             | Мария Сидорова | maria@example.com |

Вот теперь красота. Данные о клиенте в одном-единственном месте. Поменял почту в Customers — и она автоматом актуальна для всех его заказов. В Node.js с тем же Sequelize это будет две модели: Order и Customer, связанные через Customer.hasMany(Order) и Order.belongsTo(Customer). Чисто, логично, и аномалий обновления — как не бывало. В общем, не пренебрегай нормализацией, а то потом сам от себя охуеешь, когда начнётся ад с данными.