Урок 59 из 84 · Базы данных
Вторая нормальная форма
После предыдущих манипуляций мы получили такую таблицу:
order_items
| first_name | item | address | price | last_name | id |
|---|---|---|---|---|---|
| Самат | утюг | Бишкек, ул. Интергельпо | 1000.00 | Мусаев | 8 |
| Айбек | кофеварка | Ош, ул. Айтматова | 5000.00 | Темиров | 2 |
| Манас | утюг | Талас, ул. Манаса | 1000.00 | Саматов | 7 |
| Манас | телевизор | Талас, ул. Манаса | 6500.00 | Саматов | 4 |
| Самат | ноутбук | Каракол, ул. Токомбаева | 20000.00 | Мусаев | 9 |
| Самат | ноутбук | Каракол, ул. Токомбаева | 20000.00 | Мусаев | 6 |
Вторая нормальная форма включает в себя два пункта:
- Таблица должна быть в первой нормальной форме
- Все атрибуты (не ключевые) таблицы должны зависеть от первичного ключа
Зависимость от первичного ключа означает, что этот атрибут имеет такое значение именно в данном контексте. Предположим, что в примере выше, Самат это всегда один и тот же человек, делающий заказ на разные адреса. В таком случае видно, что адрес привязан к конкретному заказу. Это и есть зависимость от первичного ключа. А вот имя пользователя и его фамилия с заказом никак не связано. Оно имеет отношение к самому пользователю.
Согласно второй форме, атрибуты first_name и last_name необходимо вынести в свою таблицу, которая будет отвечать за пользователей:
users
| id | last_name | first_name |
|---|---|---|
| 2 | Мусаев | Самат |
| 3 | Темиров | Айбек |
| 5 | Саматов | Манас |
В этой таблице всего три записи, потому что у нас три уникальных пользователя. Каждому из этих пользователей присваивается свой уникальный номер (первичный ключ).
Теперь нужно связать таблицу order_items с таблицей users. Делается это через указание первичных ключей в зависимых таблицах. Ниже пример:
order_items
| user_id | item | address | price | id |
|---|---|---|---|---|
| 2 | утюг | Бишкек, ул. Интергельпо | 1000.00 | 8 |
| 3 | кофеварка | Ош, ул. Айтматова | 5000.00 | 2 |
| 5 | утюг | Талас, ул. Манаса | 1000.00 | 7 |
| 5 | телевизор | Талас, ул. Манаса | 6500.00 | 4 |
| 2 | ноутбук | Каракол, ул. Токомбаева | 20000.00 | 9 |
| 2 | ноутбук | Каракол, ул. Токомбаева | 20000.00 | 6 |
Мы удалили first_name, last_name и добавили user_id. В этом поле хранятся идентификаторы пользователей, а само поле называется внешним ключом (или вторичным).
Такую же операцию нужно произвести и с товаром. Вынесем item в свою таблицу:
goods
| id | name |
|---|---|
| 50 | утюг |
| 30 | кофеварка |
| 20 | телевизор |
| 33 | ноутбук |
order_items
| user_id | good_id | address | price | id |
|---|---|---|---|---|
| 2 | 50 | Бишкек, ул. Интергельпо | 1000.00 | 8 |
| 3 | 30 | Ош, ул. Айтматова | 5000.00 | 2 |
| 5 | 50 | Талас, ул. Манаса | 1000.00 | 7 |
| 5 | 20 | Талас, ул. Манаса | 6500.00 | 4 |
| 2 | 33 | Каракол, ул. Токомбаева | 20000.00 | 9 |
| 2 | 33 | Каракол, ул. Токомбаева | 20000.00 | 6 |

Важно понимать, что внешний ключ — это не ссылка. Таблицы существуют сами по себе, и во внешнем ключе указывается конкретное значение, которое должно совпадать с первичным ключом другой таблицы.
Синтаксис определения вторичного (внешнего) ключа:
REFERENCES <название таблицы на которую смотрим> (<список полей в той таблице, которым соответствуем>)
-- Внешних ключей может быть любое количество: сколько ссылок — столько и ключей
CREATE TABLE orders (
id bigint PRIMARY KEY,
-- Тип внешнего ключа должен быть такой же,
-- как у первичного в той таблице, куда ссылается внешний
user_id bigint REFERENCES users (id),
-- остальные поля
);
Для чего это нужно? Таким образом автоматически поддерживаются гарантии корректности. Например, невозможно удалить запись из основной таблицы, если на эту запись есть ссылки из внешних ключей в другой таблице. Это очень важно для соблюдения целостности, чтобы случайно не завести базу данных в неконсистентное состояние (то есть такое состояние, при котором данные ссылаются на несуществующие данные).