Редактор ER-диаграмм Mermaid
ER-диаграмма показывает таблицы, их столбцы и то, как они соединяются. Она уместна, когда предмет — схема базы данных, а интересные вопросы касаются ключей и кратности: один-ко-многим, необязательная связь, связь через промежуточную таблицу. Для типов с поведением и наследованием берите диаграмму классов.
Нормализованная схема заказов со связующей таблицей
Внимания заслуживает `}o--||` у строки заказа: так честно выражается связь многие-ко-многим — через промежуточную таблицу с собственными столбцами, а не притворством, будто две таблицы соединяются напрямую. Хранение цены в строке заказа, а не только в товаре, — то самое решение, которое диаграмма и должна показывать.
erDiagram
КЛИЕНТ ||--o{ ЗАКАЗ : оформляет
ЗАКАЗ ||--|{ СТРОКА_ЗАКАЗА : содержит
ТОВАР ||--o{ СТРОКА_ЗАКАЗА : "встречается в"
КЛИЕНТ ||--o{ АДРЕС : "доставка по"
КЛИЕНТ {
int id PK
string почта UK "хранится в нижнем регистре"
string фио
datetime создан
}
ЗАКАЗ {
int id PK
int клиент_id FK
string статус
decimal сумма
}
СТРОКА_ЗАКАЗА {
int заказ_id PK, FK
int товар_id PK, FK
int количество
decimal цена "цена на момент заказа"
}
ТОВАР {
int id PK
string артикул UK
string название
}
АДРЕС {
int id PK
int клиент_id FK
string индекс
}Разобранные примеры
1. Две сущности и одна связь
Каждой связи в ER нужна подпись после двоеточия — в отличие от ребра блок-схемы, она не необязательна. Читается как предложение: КЛИЕНТ оформляет ЗАКАЗ.
erDiagram
КЛИЕНТ ||--o{ ЗАКАЗ : оформляет2. Добавляем столбцы
Каждая строка атрибута — это `тип имя`, и обе части обязательны. `PK`, `FK` и `UK` отмечают ключи; строка в кавычках в конце — это комментарий.
erDiagram
КЛИЕНТ ||--o{ ЗАКАЗ : оформляет
КЛИЕНТ {
int id PK
string почта UK
string фио
}
ЗАКАЗ {
int id PK
int клиент_id FK
datetime оформлен "в UTC"
}3. Кратность, которая что-то сообщает
Два символа, ближайшие к каждой сущности, — это её кратность. Этот пример говорит, что в заказе обязана быть хотя бы одна строка, но у клиента может не быть ни одного заказа: реальное ограничение, которое схема должна соблюдать, а диаграмма — показывать.
erDiagram
КЛИЕНТ ||--o{ ЗАКАЗ : оформляет
ЗАКАЗ ||--|{ СТРОКА_ЗАКАЗА : содержит
СТРОКА_ЗАКАЗА }o--|| ТОВАР : ссылается4. Многие-ко-многим через связующую таблицу
Mermaid умеет рисовать прямую связь многие-ко-многим, но в схеме её почти никогда нет: под ней лежит промежуточная таблица. Нарисовать её честнее, и появляется место для столбцов, принадлежащих самой связи.
erDiagram
СТУДЕНТ ||--o{ ЗАПИСЬ : "записывается на"
КУРС ||--o{ ЗАПИСЬ : "изучается через"
ЗАПИСЬ {
int студент_id PK, FK
int курс_id PK, FK
datetime записан
string оценка "пусто до аттестации"
}
СТУДЕНТ {
int id PK
string фио
}
КУРС {
int id PK
string код UK
}5. Связь с самой собой
Таблица может быть связана сама с собой: линия подчинения, дерево категорий, ветка комментариев. Подпись связи здесь важнее обычного, потому что оба конца — одна и та же сущность.
erDiagram
СОТРУДНИК ||--o{ СОТРУДНИК : руководит
СОТРУДНИК {
int id PK
int руководитель_id FK "пусто у директора"
string фио
string должность
}Справочник синтаксиса ER-диаграммы
Кратность записывается двумя символами на каждом конце связи и читается от середины наружу. Левая пара описывает левую сущность, правая — правую.
| Синтаксис | Значение |
|---|---|
| erDiagram | Открывает диаграмму. Регистр важен: `erdiagram` не работает. |
| A ||--o{ B : подпись | Связь. Подпись обязательна. |
| || | Ровно один. |
| o| | Ноль или один. |
| }| | Один или больше. |
| }o | Ноль или больше. |
| -- | Идентифицирующая связь — сплошная линия. |
| .. | Неидентифицирующая связь — пунктир. |
| A { ... } | Блок атрибутов сущности A. |
| int id PK | Атрибут: тип, затем имя, затем необязательная пометка ключа. |
| PK / FK / UK | Первичный, внешний, уникальный ключ. |
| int id PK, FK | Несколько пометок ключа через запятую. |
| string почта "комментарий" | Строка в кавычках в конце — комментарий к столбцу. |
| A ||--o{ B : "принадлежит к" | Кавычки ОБЯЗАТЕЛЬНЫ, как только в подписи есть пробел. Без них подпись обрывается на первом пробеле, а каждое оставшееся слово становится лишней сущностью. |
Шесть ошибок, ломающих ER-диаграмму
У ER самая строгая грамматика из шести типов: то, что проходит в других диаграммах, здесь отвергается сразу. Но есть и тихая ошибка, и по-русски она встречается чаще, чем кажется. Всё воспроизведено на Mermaid 11.12.2.
Что вы видите
Диаграмма рисуется, подпись обрезана, а на схеме появились неизвестные сущности
Почему
Подпись связи с пробелом и без кавычек. По-русски однословные глаголы вроде «оформляет» или «содержит» спасают часто, но не всегда: «принадлежит к», «встречается в», «доставка по» — обычные для схемы формулировки. Измерено: `КЛИЕНТ ||--o{ ЗАКАЗ : принадлежит к` не даёт ошибки, обрезает подпись до «принадлежит» и создаёт пустую сущность с именем «к». Две таблицы рисуются тремя прямоугольниками, и ничего об этом не сообщает.
Решение
Берите в кавычки любую подпись с пробелом. Проще сделать это правилом, чем каждый раз решать заново.
erDiagram
КЛИЕНТ ||--o{ ЗАКАЗ : принадлежит кerDiagram
КЛИЕНТ ||--o{ ЗАКАЗ : "принадлежит к"Что вы видите
Parse error, заканчивается на: Expecting 'COLON', 'STYLE_SEPARATOR', got 'NEWLINE'
Почему
Связь без подписи. В отличие от ребра блок-схемы, двоеточие и подпись обязательны — без них строка просто заканчивается слишком рано.
Решение
Добавьте двоеточие и глагол, в кавычках, если в подписи есть пробел.
erDiagram
КЛИЕНТ ||--o{ ЗАКАЗerDiagram
КЛИЕНТ ||--o{ ЗАКАЗ : оформляетЧто вы видите
Parse error, заканчивается на: got 'UNICODE_TEXT'
Почему
Недопустимый токен кратности. Допустимы только `||`, `o|`, `}|` и `}o` (и их зеркальные варианты); всё остальное падает. `oo` — обычная опечатка, от ожидания, что нотация симметрична.
Решение
Используйте одну из четырёх пар. `||--o{` — ровно один к нулю или больше — покрывает большинство внешних ключей.
erDiagram
КЛИЕНТ ||--oo{ ЗАКАЗ : оформляетerDiagram
КЛИЕНТ ||--o{ ЗАКАЗ : оформляетЧто вы видите
Parse error, заканчивается на: Expecting 'ATTRIBUTE_WORD', got 'BLOCK_STOP'
Почему
У атрибута есть имя, но нет типа. Атрибуты ER записываются как `тип имя`, и тип не является необязательным — это удивляет тех, кто пришёл из баз без схемы.
Решение
Дайте тип каждому атрибуту. Придумайте его, если в реальной схеме типа нет: `string`, `int`, `json`.
erDiagram
КЛИЕНТ {
почта
}erDiagram
КЛИЕНТ {
string почта
}Что вы видите
Parse error, заканчивается на: Expecting 'NON_IDENTIFYING', 'IDENTIFYING', got 'UNICODE_TEXT'
Почему
Неверная линия между двумя пометками кратности. Она должна быть ровно из двух символов: `--` для идентифицирующей связи или `..` для неидентифицирующей. Один дефис — это не сокращённая запись `--`, а синтаксическая ошибка.
Решение
Между парами кратности пишите `--` или `..`.
erDiagram
КЛИЕНТ ||-o{ ЗАКАЗ : оформляетerDiagram
КЛИЕНТ ||--o{ ЗАКАЗ : оформляетЧто вы видите
Кратность рисуется, но описывает противоположное ограничение
Почему
Два символа, ближайшие к сущности, принадлежат именно ей, и прочитать их наоборот очень легко. `КЛИЕНТ ||--o{ ЗАКАЗ` говорит, что у одного клиента ноль или больше заказов. Переверните в `КЛИЕНТ }o--|| ЗАКАЗ` — и вы заявили, что каждый клиент принадлежит ровно одному заказу, что бессмысленно, и при этом рисуется без возражений.
Решение
Читайте от середины наружу: значки, касающиеся КЛИЕНТА, описывают, сколько клиентов, а не сколько заказов.
erDiagram
КЛИЕНТ }o--|| ЗАКАЗ : оформляетerDiagram
КЛИЕНТ ||--o{ ЗАКАЗ : оформляетЗаметки о рендеринге
Измерено на том Mermaid 11.12.2, который использует сайт.
Раскладку определяют связи, а не количество сущностей
Цепочка сущностей, где каждая связана со следующей, рисуется высоким столбцом: сорок сущностей дают viewBox около 116×7315 против 116×470 при трёх. Но те же сорок сущностей, связанных с одной центральной таблицей, дают нечто гораздо более широкое и низкое. Это единственный тип диаграмм здесь, где форма ваших данных меняет форму картинки сильнее, чем её объём: схему, которая не помещается, чаще удаётся починить сменой того, какие связи вы рисуете, а не их количества.
Кириллица работает, но заглавные буквы теряют роль подсказки
Проверено: `КЛИЕНТ`, `почта` и `фио` работают как имена сущностей, столбцов и типов. Но традиция писать сущности заглавными в английском полезна ещё и тем, что делает заметной случайную ссылку строчными: `CUSTOMER` и `customer` — две разные сущности и два прямоугольника без предупреждения. То же самое верно и для кириллицы, и вдобавок у нас есть свой источник расхождений — буква ё: `ЗАЁМ` и `ЗАЕМ` дадут две разные таблицы. Решите написание сущностей заранее и держитесь его.
Грамматика самая строгая из шести типов
Подписи связей обязательны, типы атрибутов обязательны, а токены кратности образуют закрытый набор. По сравнению с Гантом, где рисуется почти всё и ошибки молчат, ER падает рано и громко. Это достоинство: если ER-диаграмма нарисовалась, велика вероятность, что это именно та диаграмма, которую вы имели в виду, — за заметным исключением подписей без кавычек.
Подписи — это HTML, поэтому экспорт в PNG перерисовывает
Таблицы атрибутов сущностей рисуются внутри `<foreignObject>` в SVG, поэтому браузеры не могут растрировать их на canvas напрямую. Раньше экспорт в PNG на этом сайте молча отдавал SVG-файл; теперь он сначала перерисовывает диаграмму подписями обычным SVG-текстом и выдаёт правильный PNG в полном размере, с едва заметно иной типографикой.
Тема меняет цвета, но никогда — геометрию
Светлая и тёмная темы дают одинаковый viewBox для одного исходника, так что таблицы атрибутов не перекомпоновываются и не обрезаются при смене темы.
Когда лучше взять другую диаграмму
Если нужны наследование, интерфейсы или методы, это неподходящая диаграмма: в ER нет ни одного из этих понятий. Возьмите диаграмму классов и примите, что она моделирует ваш код, а не ваши таблицы.
Если схема большая, ER-диаграмма всей схемы превращается в настенный плакат, который никто не читает. Нарисуйте пять таблиц, относящихся к тому, что вы объясняете, а остальное оставьте за кадром: диаграмма — это довод, а не опись.
А если настоящий вопрос в том, как данные движутся между системами, а не как они хранятся, ответ даст блок-схема с подграфом на каждую систему, а ER-диаграмма — нет.
Другие типы диаграмм
Автор Dominik Malsch · Обновлено: