Основы проектирования БД
Основы проектирования баз данных: этапы (концептуальное, логическое, физическое), сущности, атрибуты и типы связей (1:1, 1:N, M:N), первичные и внешние ключи, нормализация для устранения дублирования данных. Пример проектирования для интернет-магазина.
Введение: Строим фундамент для данных
Представьте, что вам поручили организовать архив компании. Вам привозят горы документов: договоры, счета, акты, письма, карточки клиентов. Как их разложить? Можно сваливать все в одну коробку – тогда найти что-то будет невозможно. Можно разложить по папкам “Клиенты”, “Договоры”, “Счета”. А внутри папок – по годам, по алфавиту.
Проектирование базы данных – это то же самое, только для цифрового мира. Вы решаете:
- Какие “сущности” будут в вашей базе (клиенты, заказы, товары).
- Какие “атрибуты” у этих сущностей (у клиента – имя, телефон, email).
- Как эти сущности связаны между собой (один клиент может сделать много заказов).
Проектирование базы данных - это процесс создания структуры базы данных: определение того, какие данные будут храниться, как они будут организованы и как связаны между собой.
Правильное проектирование – это залог того, что база данных будет быстрой, не будет содержать противоречий и ее будет легко изменять.
Этапы проектирования базы данных
Проектирование базы данных обычно проходит через три этапа.
Концептуальное проектирование
Самый верхний уровень. Мы не думаем о серверах, таблицах, типах данных. Мы думаем о предметной области на языке бизнеса.
Вопросы на этом этапе:
- “Какие сущности есть в бизнесе?” (Клиент, Заказ, Товар, Сотрудник).
- “Какие у них свойства?” (У клиента есть имя, телефон, адрес).
- “Как они связаны?” (Клиент делает заказы. Заказ состоит из товаров).
Результат: ER-диаграмма (Entity-Relationship) – схема сущностей и связей.
Логическое проектирование
Мы начинаем думать о структуре данных, но еще не привязаны к конкретной СУБД. Определяем:
- Какие будут таблицы.
- Какие поля в каждой таблице.
- Какие первичные ключи.
- Какие связи между таблицами.
Результат: логическая схема базы данных (без привязки к конкретной технологии).
Физическое проектирование
Привязываемся к конкретной СУБД (PostgreSQL, MySQL, Oracle). Определяем:
- Типы данных (INTEGER, VARCHAR, DATE…).
- Индексы (что будем ускорять).
- Ограничения (какие значения допустимы).
- Параметры хранения.
Результат: готовая база данных, в которой можно создавать таблицы.
Сущности, атрибуты и связи
Это три кита, на которых держится проектирование баз данных.
Сущность (Entity)
Сущность – это объект реального мира, о котором мы хотим хранить данные. Сущность – это “что-то”, что имеет самостоятельное значение для бизнеса.
Примеры сущностей:
- Клиент
- Заказ
- Товар
- Сотрудник
- Поставщик
В базе данных сущность обычно становится таблицей.
Атрибут (Attribute)
Атрибут – это свойство, характеристика сущности. То, что мы хотим знать о сущности.
Атрибуты сущности “Клиент”:
- Имя
- Телефон
- Адрес
- Дата регистрации
В базе данных атрибут становится полем (столбцом) таблицы.
Связь (Relationship)
Связь показывает, как сущности соотносятся друг с другом.
Примеры связей:
- “Клиент” и “Заказ”: один клиент может сделать много заказов.
- “Заказ” и “Товар”: один заказ может содержать много товаров. Один товар может быть во многих заказах.
Типы связей между сущностями
Один-к-одному (One-to-One, 1:1)
Одной записи в сущности “А” соответствует ровно одна запись в сущности “Б”. И наоборот.
Примеры:
- “Человек” и “Паспорт”. У одного человека один паспорт. У одного паспорта один владелец.
- “Сотрудник” и “Кабинет”. У одного сотрудника один кабинет. В одном кабинете один сотрудник.
В базе данных: Используется редко. Обычно такие сущности объединяют в одну таблицу. Или делают две таблицы, где внешний ключ уникален.
Один-ко-многим (One-to-Many, 1:N)
Одной записи в сущности “А” соответствует много записей в сущности “Б”. Но записи в “Б” соответствует ровно одна запись в “А”.
Примеры:
- “Клиент” и “Заказ”. Один клиент может сделать много заказов. Один заказ принадлежит одному клиенту.
- “Категория” и “Товар”. Одна категория может содержать много товаров. Один товар принадлежит одной категории.
В базе данных: Самый распространенный тип. Реализуется добавлением внешнего ключа в таблицу “многих” (Заказы), который ссылается на первичный ключ таблицы “одного” (Клиенты).
Многие-ко-многим (Many-to-Many, M:N)
Многим записям в сущности “А” соответствует много записей в сущности “Б”.
Примеры:
- “Заказ” и “Товар”. Один заказ может содержать много товаров. Один товар может быть во многих заказах.
- “Студент” и “Курс”. Один студент учится на многих курсах. На одном курсе учится много студентов.
В базе данных: Реализуется через промежуточную таблицу (связующую). В этой таблице хранятся пары ключей из обеих таблиц.
Ключи в проектировании баз данных
Мы уже говорили о ключах в предыдущей части. Теперь посмотрим на них с точки зрения проектирования.
Первичный ключ (Primary Key)
Уникальный идентификатор записи в таблице.
Требования к первичному ключу:
- Уникальный (не может повторяться).
- Не пустой (не может быть NULL).
- Не меняется (или меняется очень редко).
Что может быть первичным ключом:
- Природный ключ: естественное уникальное свойство. Номер паспорта, ИНН, email. Но email может измениться, ИНН есть не у всех.
- Искусственный ключ (суррогатный): специально созданное поле, например,
id, которое автоматически увеличивается. Это самый надежный и распространенный вариант.
Внешний ключ (Foreign Key)
Поле в одной таблице, которое ссылается на первичный ключ другой таблицы.
Зачем нужен внешний ключ:
- Обеспечивает связь между таблицами.
- Гарантирует целостность данных – нельзя сослаться на несуществующую запись.
Пример: В таблице “Заказы” есть поле client_id. Это внешний ключ, который ссылается на id в таблице “Клиенты”. СУБД не даст создать заказ с client_id = 999, если клиента с таким id не существует.
Потенциальный ключ (Candidate Key)
Поле или комбинация полей, которые могли бы быть первичным ключом, но вы выбрали другой.
Пример: У сотрудника есть id (искусственный ключ) и passport_number (природный уникальный). Оба – потенциальные ключи. Вы выбрали id в качестве первичного.
Что такое нормализация
Нормализация – это процесс устранения дублирования данных и обеспечения их целостности. Это одна из самых важных тем в проектировании баз данных.
Плохая структура (денормализованная)
Представьте, что вы храните заказы вместе с данными клиента в одной таблице.
| order_id | client_name | client_phone | order_date | total |
|---|---|---|---|---|
| 101 | Иван Петров | 79001234567 | 2024-01-15 | 2500 |
| 102 | Иван Петров | 79001234567 | 2024-02-20 | 1800 |
| 103 | Мария Сидорова | 79007654321 | 2024-02-25 | 5000 |
Проблемы:
- Если Иван Петров сменит телефон, придется обновлять все его заказы. Легко ошибиться и пропустить какой-то.
- Если у Ивана Петрова будет 1000 заказов, его телефон будет храниться 1000 раз. Это трата места.
- Можно случайно написать для одного заказа “Иван Петров”, а для другого “Иван П.” – это разные люди или один?
Хорошая структура (нормализованная)
Разделяем на две таблицы.
Таблица “Клиенты”:
| client_id | name | phone |
|---|---|---|
| 1 | Иван Петров | 79001234567 |
| 2 | Мария Сидорова | 79007654321 |
Таблица “Заказы”:
| order_id | client_id | order_date | total |
|---|---|---|---|
| 101 | 1 | 2024-01-15 | 2500 |
| 102 | 1 | 2024-02-20 | 1800 |
| 103 | 2 | 2024-02-25 | 5000 |
Преимущества:
- Телефон клиента хранится один раз. Обновили в одном месте – все заказы “увидели” новые данные.
- Экономия места.
- Нет риска рассинхрона.
Простые правила нормализации
Каждая информация хранится в одном месте. Не дублируйте данные. Если телефон клиента нужен в заказе – храните ссылку на клиента, а не сам телефон.
Каждая таблица описывает одну сущность. В таблице “Заказы” не должно быть полей “Имя клиента” и “Телефон клиента”. Это данные о другой сущности – клиенте.
Проектирование на примере интернет-магазина
Выделяем сущности
Какие объекты есть в нашем бизнесе?
- Клиент – тот, кто покупает.
- Заказ – то, что создает клиент.
- Товар – то, что покупают.
- Категория – группировка товаров.
ШОпределяем атрибуты
Клиент:
- id (первичный ключ, искусственный)
- имя
- телефон
- дата регистрации
Заказ:
- id (первичный ключ)
- client_id (внешний ключ на Клиента)
- дата создания
- статус (новый, оплачен, отгружен, доставлен)
- итоговая сумма
Товар:
- id (первичный ключ)
- название
- цена
- category_id (внешний ключ на Категорию)
- остаток на складе
Категория:
- id (первичный ключ)
- название
- описание
Определяем связи
- “Клиент” и “Заказ”: один-ко-многим (один клиент – много заказов).
- “Категория” и “Товар”: один-ко-многим (одна категория – много товаров).
- “Заказ” и “Товар”: многие-ко-многим (один заказ – много товаров, один товар – много заказов). Для этого нужна промежуточная таблица.
Создаем промежуточную таблицу для связи многие-ко-многим
Таблица “Заказы_Товары” (связующая):
- order_id (внешний ключ на Заказ)
- product_id (внешний ключ на Товар)
- quantity (количество товара в заказе)
Основная структура готова. По ней уже можно создавать таблицы в СУБД.