Mermaid ER 图编辑器
实体关系图展示表、表里的列,以及它们如何关联。当主题是数据库结构、而关心的问题是主外键和基数时用它——一对多、可选、或者通过一张连接表关联。如果对象带有行为和继承,请用类图。
带连接表的规范化订单结构
订单明细这张连接表才是值得琢磨的部分:它是诚实表达多对多关系的方式——通过一张有自己列的独立表,而不是假装两张表可以直接相连。
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 | 开启该图。区分大小写。 |
| 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 复现。
你会看到
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 邮箱
}你会看到
No diagram type detected matching given configuration
原因
大小写写错。`erdiagram` 和 `ERDiagram` 都会失败,只有 `erDiagram` 可用。
解决办法
小写 e,大写 D。
erdiagram
A ||--o{ B : 拥有erDiagram
A ||--o{ B : 拥有你会看到
Parse error,结尾是:Expecting 'NON_IDENTIFYING', 'IDENTIFYING', got 'UNICODE_TEXT'
原因
两个基数记号之间的连线写错了。它必须恰好是两个字符——标识性关系用 `--`,非标识性关系用 `..`。单个短横线不是 `--` 的简写,而是语法错误。
解决办法
在两组基数记号之间使用 `--` 或 `..`。
erDiagram
客户 ||-o{ 订单 : 提交erDiagram
客户 ||--o{ 订单 : 提交你会看到
基数能渲染,表达的却是相反的约束
原因
紧挨着某个实体的两个字符属于那个实体,而这很容易读反。`客户 ||--o{ 订单` 说的是一个客户有零个或多个订单。翻成 `客户 }o--|| 订单`,你说的就变成了每个客户恰好属于一笔订单——纯属胡说,却能毫无怨言地渲染出来。
解决办法
从中间往外读:贴着“客户”的记号描述的是有多少个客户,而不是有多少笔订单。
erDiagram
客户 }o--|| 订单 : 提交erDiagram
客户 ||--o{ 订单 : 提交渲染须知
以下均为针对本站所用 Mermaid 11.12.2 的实测结果。
中文表名和列名会让属性表更紧凑
ER 图里每个实体都是一张小表格,宽度由最长的那一行 `类型 名称` 决定。中文列名通常比英文短得多——`string 邮箱` 明显短于 `string email_address`——所以中文 ER 图的实体框更窄,同一屏能塞下更多实体。要注意的是类型名一般仍保持英文(int、string、decimal),这本身是好事:它让类型和名称在视觉上自然分开。
布局由关系驱动,而不是实体数量
一串首尾相连的实体会排成高高的一列——四十个实体的 viewBox 大约是 116×7500;而同样四十个实体如果都连向一张中心表,得到的图会宽得多、矮得多。这是本站唯一一种“数据的形状比数据的数量更能改变图的形状”的图类型。所以一张塞不下的结构图,往往靠改变要画哪些关系来解决,而不是画多少个。
它的语法是六种图里最严格的
关系标签必需、属性类型必需、基数记号是一个封闭集合。和甘特图那种几乎什么都能渲染、错误却悄无声息的风格相比,ER 图失败得又早又响。这是优点:一张 ER 图只要画出来了,它就极有可能正是你想表达的那张。
标签是 HTML,所以 PNG 导出会重新渲染
实体的属性表格画在 SVG 的 `<foreignObject>` 里,浏览器无法直接把它光栅化到 canvas 上。本站的 PNG 导出过去会悄悄退回成 SVG 文件;现在它会先用纯 SVG 文本标签重新渲染,产出尺寸正确的 PNG,只是字体排版略有差异。
实体名区分大小写,习惯上全大写
`CUSTOMER` 和 `customer` 是两个不同的实体,在同一张图里同时用到就会悄悄多出一个方框。用中文实体名时不存在大小写问题,但如果你混用了中英文命名,同样要留意同一个实体不要出现两种写法。
什么时候该换一种图
如果你需要继承、接口或方法,那这是错的图——ER 完全没有这些概念。请用类图,并接受它建模的是你的代码而不是你的表。
如果结构很大,把整个库画成一张 ER 图就成了没人会看的挂图。只画你正要解释的那五张表,其余留白;图是一个论点,不是一份清单。
而如果真正的问题是数据如何在系统之间流动、而不是它如何存储,那么按系统分子图的流程图能回答它,ER 图不能。
其他图类型
作者 Dominik Malsch · 最后更新: