Как правильно спроектировать базу
219 мин
В хорошо спроектированной базе каждый факт хранится в одном месте. В плохо спроектированной одно и то же повторяется десятки раз и со временем начинает противоречить самому себе. В этом уроке — как правильно разделить базу.
Плохой пример
Магазин ведёт всё в одной таблице:
| order_id | customer | customer_phone | products | total |
|---|---|---|---|---|
| 1 | Aziz Karimov | 901112233 | iPhone 15, AirPods 3 | 13 880 000 |
| 2 | Aziz Karimov | 901112233 | Quvvatlagich 20W | 190 000 |
| 3 | Malika Yusupova | 935556677 | iPhone 15 | 11 490 000 |
Проблемы:
- Повторы — телефон Азиза записан в каждом заказе заново. Сменится телефон — менять везде; забудете одно место — база будет «знать» два разных телефона.
- Список в одной ячейке — в
productsнесколько товаров. На вопрос «сколько продано iPhone?» средствами SQL не ответить. - Вычисляемое значение —
totalвыводится из цен товаров; они могут перестать совпадать.
Нормальные формы
Правила правильного устройства базы называются нормальными формами. На практике хватает первых трёх.
1НФ — в каждой ячейке одно значение. Никаких списков и повторяющихся столбцов (phone1, phone2, phone3). Товары разносятся по отдельным строкам.
2НФ — каждый столбец зависит от всего ключа. Если ключ составной (например, order_id + product_id), то название товара зависит только от product_id — значит, ему место в другой таблице (products).
3НФ — столбцы не зависят друг от друга. Телефону покупателя не место в заказе — он относится не к заказу, а к покупателю. Всё о покупателе — в таблице customers.
Правильная структура
Как в нашей базе shop:
customers— покупатели (имя, телефон — один раз);products— товары (название, цена);orders— заказ: кто и когда (customer_id);order_items— состав заказа: какого товара сколько (order_id,product_id,quantity).
Теперь «телефон Азиза» хранится в одном месте, а «сколько продано iPhone» — простой запрос:
Почему цена есть и в order_items?
products.price — текущая цена, order_items.unit_price — цена на момент продажи. Это не повтор: если цена завтра изменится, сумма прошлого заказа меняться не должна. Нормализация — это не «ничего не повторять», а «один смысл — в одном месте».
Денормализация
Иногда ради скорости данные повторяют намеренно: например, users.xp — готовое число вместо того, чтобы каждый раз суммировать xp_events. Это должно быть осознанным решением с понятным способом поддерживать значение актуальным.
Практические советы по проектированию
- Сначала определите сущности: студент, группа, предмет, оценка.
- Каждая — отдельная таблица, в каждой
id. - «Один ко многим» — внешний ключ на стороне «многих».
- «Многие ко многим» — промежуточная таблица.
- Имена столбцов понятные и в одном стиле:
created_at,student_id.
Выведите состав заказа номер 1 из order_items: название товара (products.name), quantity и unit_price.
Запомните
- Один смысл — одно место: повторы ведут к ошибкам.
- 1НФ — одно значение в ячейке; 2НФ — зависимость от всего ключа; 3НФ — столбцы зависят только от ключа.
- Историческое значение (цена на момент продажи) — не повтор.