Кафедра ИТКафедра ИТ
Блог
Обучение
  • О кафедре
  • Направления подготовки
  • Друзья и партнеры
  • Структура кафедры
  • Обращение к студентам
  • Официальный сайт «ВШП»
GitHub
Блог
Обучение
  • О кафедре
  • Направления подготовки
  • Друзья и партнеры
  • Структура кафедры
  • Обращение к студентам
  • Официальный сайт «ВШП»
  • ИТ.03 - 11 - Модели БД и способы их представления. ER-диаграммы

  1. Главная
  2. Учебные материалы
  3. ИТ.03 - Основы проектиро...
  4. Модели БД и способы их п...

ИТ.03 - 11 - Модели БД и способы их представления. ER-диаграммы

Введение

На предыдущих занятиях мы работали только с одной таблицей, но в реальных системах данные всегда разбиваются на несколько связанных сущностей. Чтобы эти связи были продуманными, структуру базы данных сначала проектируют в виде модели.

В этой теме разберём:

  • что такое модель данных и зачем она нужна;
  • уровни моделей (концептуальный, логический, физический);
  • элементы ER-модели: сущности, атрибуты, связи и их кратности;
  • как построить простую ER-диаграмму в нотации Mermaid и как она помогает при создании связанных таблиц.

Примечание

ER-диаграмма не заменяет саму базу данных. Это проект, который позволяет обсудить структуру предметной области и проверить её перед реализацией в SQL.

Зачем нужны модели данных

Модель данных описывает, какую информацию мы храним и как объекты связаны между собой. Она:

  • даёт всем участникам проекта единое понимание предметной области;
  • служит основой для создания таблиц и ограничений в СУБД;
  • помогает выявить лишние связи и дубли ещё до написания SQL-кода;
  • показывает зоны, в которых придётся выполнять JOIN, INSERT в несколько таблиц и т.д.

Инфо

Часто модель разрабатывают итеративно. Сначала фиксируют ключевые сущности и связи, потом уточняют атрибуты и ограничения. Не нужно пытаться описать всю систему за один подход — важно добиться согласованности базовой структуры.

Уровни моделей

УровеньЦельРезультат
КонцептуальныйВыявить основные сущности и их связи на языке предметной областиER-диаграмма, текстовое описание
ЛогическийУточнить атрибуты, ключи, ограничения и возможные типы данныхТаблицы с полями и ограничениями
ФизическийПодстроить модель под конкретную СУБД, синтаксис, схемы храненияSQL-скрипт, настройки сервера

Концептуальный уровень

На концептуальном уровне фиксируем ключевые сущности и связи между ними, без деталей по типам данных.

Посмотреть код диаграммы
```mermaid
flowchart LR
  %% Сущности
  user["Клиент"]
  order{"Заказ"}
  product["Товар"]

  %% Атрибуты
  name([Атрибут: ФИО])
  price([Атрибут: Цена])
  in_stock([Атрибут: В наличии])
  status([Атрибут: Статус])
  quantity([Атрибут: Количество])

  %% Связи
  user --- order --- product

  user -.- name
  product -.- price
  product -.- in_stock
  order -.- status
  order -.- quantity
```

Сущности — прямоугольники, атрибуты — овалы, связи — линии.

Логический уровень

Логическая модель уже описывает атрибуты, ключи, кратности и ограничения (но ещё не привязана к конкретной СУБД).

Посмотреть код диаграммы
```mermaid
erDiagram
  customer["Клиент"] {
    INTEGER id PK
    STRING full_name
    STRING phone
  }
  service["Услуга"] {
    INTEGER id PK
    STRING title
    DECIMAL base_price
  }
  order["Заказ"] {
    INTEGER id PK
    INTEGER customer_id FK
    INTEGER employee_id FK
    DATE created_at
  }
  employee["Сотрудник"] {
    INTEGER id PK
    STRING full_name
    STRING role
  }
  order_service["Заказ-Услуга"] {
    INTEGER id PK
    INTEGER order_id FK
    INTEGER service_id FK
    DECIMAL price
  }

  customer ||--o{ order : ""
  employee ||--o{ order : ""
  order ||--o{ order_service : ""
  service ||--o{ order_service : ""
```

Физический уровень

Когда структура согласована, переносим её в SQL: выбираем конкретные типы данных, добавляем индексацию, каскадные правила, комментарии.

CREATE TABLE customers (
  id INTEGER PRIMARY KEY,
  full_name TEXT NOT NULL,
  phone TEXT
);

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL REFERENCES customers(id),
  employee_id INTEGER NOT NULL REFERENCES employees(id),
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);

CREATE TABLE employees (
  id INTEGER PRIMARY KEY,
  full_name TEXT NOT NULL,
  role TEXT CHECK (role IN ('admin', 'manager'))
);

На физическом уровне мы учитываем особенности конкретной СУБД (типы данных, индексы и ограничения и т.п.).

ER-диаграмма и нотации

ER-диаграмма (Entity-Relationship Diagram) — графическое описание модели. На ней показывают сущности, атрибуты и связи между ними. Чтобы диаграммы “читались” одинаково разными специалистами, используются нотации — наборы правил, которые описывают, как изображать сущности, атрибуты и кратности. Перечислим самые известные нотации и приведём примеры.

Нотация Питера Чена

Сущности рисуют прямоугольниками, атрибуты — овалами, связи — ромбами. Это самая «классическая» форма ER-диаграмм, удобная для обсуждения предметной области без технических деталей.

Пример нотации Чена
Пример нотации Чена

Историческая справка

Нотацию предложил Питер Чен (Peter Chen) в 1976 году в статье «The Entity-Relationship Model – Toward a Unified View of Data». Именно она ввела понятия «сущность», «связь», «атрибут», которые мы используем до сих пор.

Нотация Crow's Foot

Связи изображаются линиями с «вороньими лапками», показывающими кратности (1 — 1, 1 — N, M — N). Сущности — прямоугольники с атрибутами. Это самый популярный вариант в современных инструментах.

Пример нотации Crow's Foot
Пример нотации Crow's Foot

Нотации IDEF1X, UML и другие

Используются в корпоративных методологиях или CASE-средствах. Они добавляют собственные символы для первичных/внешних ключей, жизненных циклов сущности, наследования.

Пример нотации IDEF1X
Пример нотации IDEF1X
Пример нотации UML
Пример нотации UML

Нотация Mermaid

Мы будем использовать нотацию Mermaid (подвид Crow's Foot на языке Mermaid). Диаграммы легко создать на сайте mermaid.live, где можно вводить текстовую запись, а система автоматически построит граф. Преимущества такой записи:

  • диаграмма хранится как текст, её легко версионировать;
  • не нужно привязываться к конкретной СУБД;
  • одну и ту же диаграмму можно встроить в документацию, репозиторий или показать в браузере.

Инфо

Существуют графические редакторы (drawDB, dbdiagram, drawIO, chartDB, quickDBD, drawSQL и множество других), которые позволяют рисовать ERD мышкой и экспортировать их в SQL. Когда перейдём к MySQL, познакомимся с MySQL Workbench: этот инструмент умеет строить ERD, синхронизировать их с готовой схемой БД и генерировать SQL-код.

Сущности и атрибуты

  • Сущность (Entity) — объект предметной области, о котором нужно хранить данные (например, Студент, Курс, Заказ).
  • Атрибут (Attribute) — характеристика сущности (ФИО студента, номер курса, цена заказа).

Пример описания сущности:

Посмотреть код диаграммы
```mermaid
erDiagram
  student["Студент"] {
    INTEGER   id PK               "Первичный ключ"
    STRING    first_name          "Имя"
    STRING    last_name           "Фамилия"
    INTEGER   group_id FK         "Группа"
    ENUM      gender              "Пол (м/ж)"
    BOOL      is_active           "Учится / отчислен"
    DATE      date_of_birth       "Дата рождения"
    DATE      date_of_assignment  "Дата зачисления"
    DATE      date_of_graduation  "Дата окончания"
  }
```

Заметка

На диаграмме мы указываем только логические типы полей. Позже при переводе в SQL выберем конкретные типы, поддерживаемые нашей СУБД.

Связи и кратности

Связь (Relationship) показывает, как сущности взаимодействуют. Для каждой связи важно задать кратности (cardinality) — сколько экземпляров сущности участвует.

ОбозначениеОписаниеПример
1 — 1один к одномуПаспорт ↔ человек
1 — Nодин ко многимГруппа ↔ студенты
M — Nмногие ко многимСтуденты ↔ курсы

В нотации Mermaid кратности обозначаются символами:

СлеваСправаЗначение
|oo|Ноль или один
||||Строго один
}oo{Ноль или несколько
}||{Один или несколько
Посмотреть код диаграммы
```mermaid
erDiagram
  student["Студент"] {
    INTEGER   id PK         "Первичный ключ"
    STRING    first_name    "Имя"
    STRING    last_name     "Фамилия"
    INTEGER   group_id FK   "Группа"
  }
  group["Группа"] {
    INTEGER   id PK         "Первичный ключ"
    STRING    title         "Название группы"
  }

  group ||--o{ student : "содержит"
```

Совет

Проверяйте связи вопросами: «Сколько студентов может быть в группе?» и «Сколько групп может быть у студента?». Ответы определяют кратность по каждой стороне.

Определяющие и не определяющие связи

В ER-моделях выделяют два типа связей:

  • Определяющая (identifying) — дочерняя сущность не существует без родительской. Обычно её первичный ключ включает ключ родителя. На диаграмме связь рисуют сплошной линией.
  • Не определяющая (non-identifying) — сущность существует сама по себе, а внешний ключ добавляется только для связи. На диаграмме это пунктирная линия.

При разработке важно понимать характер отношения: определяющие связи подсказывают, что значение родители должно входить в состав уникального ограничения у дочерней сущности.

В левом блоке «Заявка» имеет собственный ключ, а ссылки на клиента и менеджера нужны лишь для связи. В правом блоке дочерние сущности «Позиция заказа» и «Счёт» живут только в контексте конкретного заказа — их ключи зависят от родительского «Заказа».

ER-диаграмма для простого интернет-магазина

Возьмём ограниченную предметную область: покупатель оформляет заказы, заказ состоит из строк с товарами.

Посмотреть код диаграммы
```mermaid
erDiagram
  customer["Покупатель"] {
    INTEGER id PK                "Первичный ключ"
    STRING full_name             "ФИО"
    STRING email                 "Электронная почта"
  }

  order["Заказ"] {
    INTEGER id PK                "Первичный ключ"
    INTEGER customer_id FK       "Покупатель"
    DATE created_at              "Дата создания"
    STRING status                "Статус"
  }

  order_item["Детали заказа"] {
    INTEGER id PK                "Первичный ключ"
    INTEGER order_id FK          "Заказ"
    INTEGER product_id FK        "Товар"
    INTEGER quantity             "Количество"
    DECIMAL unit_price           "Цена"
  }

  product["Товар"] {
    INTEGER id PK                "Первичный ключ"
    STRING title                 "Название"
    DECIMAL price                "Текущая цена"
  }

  customer ||--o{ order : "оформляет"
  order ||--o{ order_item : "содержит"
  product ||--o{ order_item : "используется"
```
  • Покупатель связан с Заказом как 1 — N: один человек может оформить много заказов, но каждый заказ принадлежит только ему.
  • Заказ и Товар взаимодействуют через сущность Детали заказа. Это типичная реализация связи M — N, превращённая в две связи 1 — N.
  • На этапе реализации каждую сущность превратим в таблицу, а поля id станут первичными ключами. В таблице деталей заказа появятся внешние ключи order_id, product_id.

Как перейти от ER-диаграммы к таблицам

  1. Таблица = сущность. Для каждого прямоугольника на диаграмме создаём таблицу. Колонки берём из атрибутов.
  2. Первичные ключи. Поле, помеченное как PK, становится первичным ключом (PRIMARY KEY). Если ключ не указан, его нужно выбрать (обычно автоинкрементное id).
  3. Внешние ключи. Для каждого ребра 1 — N добавляем в таблицу «многие» колонку со ссылкой на «один»: например, orders.customer_id REFERENCES customers(id).
  4. Связи M — N. Разрываем на две связи 1 — N через промежуточную таблицу (как order_items). В неё переносим дополнительные атрибуты (количество, цены).
  5. Типы данных и ограничения. Для каждого поля выбираем подходящий тип SQLite/MySQL, добавляем NOT NULL, UNIQUE, значения по умолчанию и т.д.
  6. Дополнительные параметры. На физическом уровне можно задать действия при удалении/обновлении (ON DELETE/UPDATE) и заранее продумать, какие поля потребуется ускорять каждым типом запросов (к этому вернёмся позже).

Инфо

Всегда сверяйте атрибуты и связи на диаграмме с планом реализации: так проще убедиться, что в SQL-скрипте не потерян внешний ключ или не перепутана кратность.

Инструменты для построения ERD

Mermaid.live

Интерфейс Mermaid.live
Интерфейс Mermaid.live

mermaid.live — текстовый редактор диаграмм. Пишем код на языке Mermaid (как в примерах лекции) и сразу видим визуализацию. Плюсы: диаграммы легко хранить в текстовом виде, есть подсветка синтаксиса, можно экспортировать в PNG/SVG.

drawDB

Интерфейс drawDB
Интерфейс drawDB

drawDB — графический конструктор, в котором сущности и связи рисуются мышкой. Работает в браузере и офлайн (есть версии для сборки при помощи Docker), поддерживает выгрузку диаграммы сразу в SQL (SQLite, MySQL и др.), а также импорт/экспорт проектов.

Для упражнений ниже можно выбрать любой инструмент: если нравится писать текстом — используйте Mermaid; если удобнее графически — drawDB.

Практические задания

Задание 1. Концептуальная диаграмма

Условие

Опишите на концептуальном уровне предметную область «онлайн-курсы». Сущности: «Преподаватель», «Курс», «Студент», «Запись на курс». Постройте простую схему (Mermaid flowchart, рисунок от руки или диаграмма в drawDB) без указания атрибутов и ключей.

Решение
Посмотреть код диаграммы
```mermaid
flowchart LR
  teacher[Преподаватель]
  course[Курс]
  enrollment[Запись на курс]
  student[Студент]

  teacher --- course
  course --- enrollment
  student --- enrollment
```

На концептуальном уровне фиксируем только сущности и связи между ними: преподаватель создаёт курсы, курс имеет записи, а студент участвует через сущность «Запись на курс». Атрибуты и ключи пока не нужны.

Задание 2. Логическая диаграмма

Условие

Продолжите работу с предметной областью «онлайн-курсы». В учебной платформе один преподаватель ведёт несколько курсов, а каждый студент может записаться на разные курсы. Постройте логическую ER-диаграмму (mermaid.live или drawDB), где:

  • у Преподавателя (teacher) есть атрибуты id, ФИО, контакт;
  • у Курса (course) хранится название и внешний ключ teacher_id;
  • факт записи фиксируется в отдельной сущности Запись на курс (enrollment) с внешними ключами student_id и course_id;
  • в Студенте (student) достаточно ФИО и группового кода.

То есть нужно показать классическую схему «преподаватель ↔ курс ↔ (таблица связки) ↔ студент» с явными PK и FK.

Решение
Посмотреть код диаграммы
```mermaid
erDiagram
  teacher["Преподаватель"] {
    INTEGER id PK             "Первичный ключ"
    STRING full_name          "ФИО"
    STRING email              "Контакт"
  }

  course["Курс"] {
    INTEGER id PK             "Первичный ключ"
    STRING title              "Название"
    INTEGER teacher_id FK     "Ведущий преподаватель"
  }

  student["Студент"] {
    INTEGER id PK             "Первичный ключ"
    STRING full_name          "ФИО"
    STRING group_code         "Группа"
  }

  enrollment["Запись на курс"] {
    INTEGER id PK             "Первичный ключ"
    INTEGER student_id FK     "Студент"
    INTEGER course_id FK      "Курс"
    DATE enrolled_at          "Дата записи"
  }

  teacher ||--o{ course : "ведёт"
  course ||--o{ enrollment : "имеет записи"
  student ||--o{ enrollment : "посещает"
```

Преподаватель связан с курсами как 1 — N. Связь студент ↔ курс реализуется через таблицу «Запись на курс», что позволяет хранить дату регистрации и другие атрибуты без дублирования данных.

Задание 3. Физическая реализация и проверка

Условие

По диаграмме из задания 2 напишите SQL-скрипт (SQLite), который создаёт таблицы teachers, courses, students, enrollments, настраивает ключи и проверяет структуру через PRAGMA table_info и PRAGMA foreign_key_list.

Последовательное выполнение запросов PRAGMA foreign_key_list('courses'); и PRAGMA foreign_key_list('enrollments'); должно вернуть результат:

┌────┬─────┬──────────┬────────────┬────┬───────────┬───────────┬───────┐
│ id │ seq │  table   │    from    │ to │ on_update │ on_delete │ match │
├────┼─────┼──────────┼────────────┼────┼───────────┼───────────┼───────┤
│ 0  │ 0   │ teachers │ teacher_id │ id │ NO ACTION │ NO ACTION │ NONE  │
└────┴─────┴──────────┴────────────┴────┴───────────┴───────────┴───────┘
┌────┬─────┬──────────┬────────────┬────┬───────────┬───────────┬───────┐
│ id │ seq │  table   │    from    │ to │ on_update │ on_delete │ match │
├────┼─────┼──────────┼────────────┼────┼───────────┼───────────┼───────┤
│ 0  │ 0   │ courses  │ course_id  │ id │ NO ACTION │ NO ACTION │ NONE  │
│ 1  │ 0   │ students │ student_id │ id │ NO ACTION │ NO ACTION │ NONE  │
└────┴─────┴──────────┴────────────┴────┴───────────┴───────────┴───────┘
-- Ваш код можете писать тут


Решение
CREATE TABLE teachers (
  id INTEGER PRIMARY KEY,
  full_name TEXT NOT NULL,
  email TEXT
);

CREATE TABLE courses (
  id INTEGER PRIMARY KEY,
  title TEXT NOT NULL,
  teacher_id INTEGER NOT NULL REFERENCES teachers(id)
);

CREATE TABLE students (
  id INTEGER PRIMARY KEY,
  full_name TEXT NOT NULL,
  group_code TEXT
);

CREATE TABLE enrollments (
  id INTEGER PRIMARY KEY,
  student_id INTEGER NOT NULL REFERENCES students(id),
  course_id INTEGER NOT NULL REFERENCES courses(id),
  enrolled_at TEXT DEFAULT (date('now'))
);

PRAGMA foreign_key_list('courses');
PRAGMA foreign_key_list('enrollments');

PRAGMA table_info покажет столбцы и их ограничения, а foreign_key_list — наличие ссылок на students и courses. Если результаты соответствуют ожиданиям, физическая модель реализована корректно.

Последнее обновление: 14.11.2025, 19:24
Предыдущая
ИТ.03 - 10 - Вложенные запросы: общее понимание
© Кафедра информационных технологий ЧУВО «ВШП», 2025. Версия: 0.26.9
Материалы доступны в соответствии с лицензией: