免费注册
流程类
图形化表达方式
脑图类
结构化表达方式
笔记类
高效化表达方式
实用工具
实用工具
业务与管理领域
软件工程与系统设计
UML
数据分析与研究
工程与技术设计
数据库与信息系统
树形图
括号图
思维笔记

ER图语法

ER图

<> 实体关系图(简称ER图)描述了特定知识字段中相互关联的感兴趣事物。一个基本的 ER图由实体类型(用于分类感兴趣的事物)组成,并且指定了实体(这些实体类型的实例)之间可以存在的关系,请注意,在ER图实际操作中,几乎总是将‘实体类型’简称为‘实体’。例如,’CUSTOMER 实体类型‘通常会直接称为‘CUSTOMER 实体’。这种做法非常普遍,因此不建议使用其他方式,但从技术上讲,实体是实体类型的抽象实例,ER图显示的正是这些抽象实例及它们之间的关系。这就是为什么实体总是使用单数名词命名的原因。
代码:
                            
复制
嵌入
--- title: Order example --- erDiagram CUSTOMER ||--o{ ORDER : places ORDER ||--|{ LINE-ITEM : contains CUSTOMER }|..|{ DELIVERY-ADDRESS : uses

实体名称通常是大写的,尽管对此没有公认的标准,Mermaid 语法也没有强制要求。

实体之间的关系,用带有表示基数的端点标记的线表示。Mermaid 使用最流行的乌鸦脚符号,乌鸦脚直观地表示它所连接的实体可能有多个实例。

<> ER图可用于各种用途,从不包含任何实现细节的抽象逻辑模型,到关系数据库表的物理模型。在ER图上包含属性定义有助于理解实体的用途和含义,这些属性定义不必详尽无遗,通常一小部分属性就足够了。Mermaid允许以其类型和名称来定义它们。
代码:
                            
复制
嵌入
erDiagram CUSTOMER ||--o{ ORDER : places CUSTOMER { string name string custNumber string sector } ORDER ||--|{ LINE-ITEM : contains ORDER { int orderNumber string deliveryAddress } LINE-ITEM { string productCode int quantity float pricePerUnit }

在ER图上包含属性时,你必须决定是否将外键作为属性包含,这可能取决于你希望多大程度上表示关系表结构。如果你的图表是一个逻辑模型,并不打算暗示关系实现,那么最好不要包含这些属性,因为关联关系已经传达了实体之间的关联方式。例如,JSON数据结构可以在没有外键属性的情况下使用数组实现一对多关系。类似地,面向对象的编程语言可以使用指针或对集合的引用。即使对于那些旨在进行关系实现的模型,也可能认为包含外键属性会重复展示关系展示的信息,并且不会给实体增加意义。

语法

实体和关系

ER图的Mermaid语法与PlantUML兼容,并扩展了用于标记关系的功能。每个语句包括以下内容:

                            
复制
嵌入
<first-entity> [<relationship> <second-entity> : <relationship-label>]
  • first-entity 是第一个实体的名称。名称支持任何 Unicode 字符,如果用双引号括起来,则可以包含空格。
  • relationship 描述了两个实体之间的相互关系。
  • second-entity 是另一个实体的名称。
  • relationship-label 描述了从第一个实体的角度来看关系。

例如:

                            
复制
嵌入
PROPERTY ||--|{ ROOM : contains

该声明可以理解为:一个属性包含一个或多个房间,而一个房间只属于一个属性。这里的标签是从第一个实体的角度来看的:一个属性包含一个房间,但一个房间不包含属性。从第二个实体的角度来看,等效的标签通常很容易推断出来。

只有 first-entity 部分是强制性的。这使得显示没有关系的实体成为可能,这在迭代构建图表时非常有用。如果指定了语句的其他部分,则所有部分都是必填的。

Unicode 文本

实体名称、关系和属性都支持 unicode 文本。

代码:
                            
复制
嵌入
erDiagram "This ❤ Unicode"

关系语法

每个语句的 relationship 部分可以拆分为三个子部分:

  • 第一个实体相对于第二个实体的基数
  • 该关系是否赋予 '子' 实体身份'
  • 第二个实体相对于第一个实体的基数

基数是一个属性,用来描述另一个实体可以与所涉及的实体相关联的元素数量。在上面的示例中,一个 PROPERTY 可以关联一个或多个 ROOM 实例,而一个 ROOM 只能关联一个 PROPERTY。每个基数标记中有两个字符,最外面的字符代表最大值,最里面的字符代表最小值。下表总结了基数的可能性:

值(左) 值(右) 意义
|o o| 零或一
|| || 正好一个
}o o{ 零个或多个(无上限)
}| |{ 一个或多个(无上限)
别名
值(左) 值(右) 意义
one or zero one or zero 零或一
zero or one zero or one 零或一
one or more one or more 一个或多个
one or many one or many 一个或多个
many(1) many(1) 一个或多个
1+ 1+ 一个或多个
zero or more zero or more 零个或多个
zero or many zero or many 零个或多个
many(0) many(0) 零个或多个
0+ 0+ 零个或多个
only one only one 正好一个
1 1 正好一个

身份识别

关系可分为识别性和非识别性,分别用实线或虚线渲染。当其中一个实体无法在没有另一个实体的情况下独立存在时,这很重要。 例如,一家为人们驾驶汽车提供保险的公司可能需要存储 NAMED-DRIVER 的数据。在建模时,我们可能首先观察到,一个 CAR 可以由许多 PERSON 驾驶,而一个 PERSON 可以驾驶许多 CAR ,两者都可以在没有对方的情况下存在,因此这是一个非标识型关系,我们可以在 Mermaid 中指定为:PERSON }|..|{ CAR : "driver"。 请注意关系中间的两个点,这将导致在两个实体之间绘制虚线。但是,当这种多对多关系解析为两个一对多关系时,我们观察到如果没有 PERSON 和 CAR,NAMED-DRIVER 就不可能存在。这些关系变成了识别关系,并且会用连字符表示,对应的就是一条实线:

值 别名
-- 识别
.. 非识别
别名
值 别名为
to 识别
optionally to 非识别
代码:
                            
复制
嵌入
erDiagram CAR ||--o{ NAMED-DRIVER : allows PERSON }o..o{ NAMED-DRIVER : is
代码:
                            
复制
嵌入
erDiagram CAR ||--o{ NAMED-DRIVER : allows PERSON }o--o{ NAMED-DRIVER : is

属性

可以通过指定实体名称,然后跟随一个包含多个 type name 的块来为实体定义属性,其中块由一个大括号 {} 包围。属性会显示在实体框内,例如:

代码:
                            
复制
嵌入
erDiagram CAR ||--o{ NAMED-DRIVER : allows CAR { string registrationNumber string make string model } PERSON ||--o{ NAMED-DRIVER : is PERSON { string firstName string lastName int age }

type 值必须以字母字符开头,并且可以包含数字、连字符、下划线、小括号和中括号。name 值遵循与 type 类似的格式,但也可以是星号开头,作为指示属性是主键的另一个选项。除此之外,没有其他限制,也没有隐含的有效数据类型集合。

实体别名

可以使用中括号为实体添加别名。如果提供了别名,图表中将显示别名而不是实体名称,别名遵循与实体名称相同的所有规则。

代码:
                            
复制
嵌入
erDiagram p[Person] { string firstName string lastName } a["Customer Account"] { string email } p ||--o| a : has

属性键和注释

属性也可以定义键或注释。键可以是主键(PK)、外键(FK) 或唯一键(UK)。要在单个属性上指定多个键约束,用逗号分隔它们(例如,PK, FK)。注释通过属性末尾的双引号定义,注释中不能包含双引号字符。

代码:
                            
复制
嵌入
erDiagram CAR ||--o{ NAMED-DRIVER : allows CAR { string registrationNumber PK string make string model string[] parts } PERSON ||--o{ NAMED-DRIVER : is PERSON { string driversLicense PK "The license #" string(99) firstName "Only 99 characters are allowed" string lastName string phone UK int age } NAMED-DRIVER { string carRegistrationNumber PK, FK string driverLicence PK, FK } MANUFACTURER ||--o{ CAR : makes

方向

方向语句声明图表的方向。

TB 声明图表的方向是从上到下,BT 可以将其反转为从下到上。

代码:
                            
复制
嵌入
erDiagram direction TB CUSTOMER ||--o{ ORDER : places CUSTOMER { string name string custNumber string sector } ORDER ||--|{ LINE-ITEM : contains ORDER { int orderNumber string deliveryAddress } LINE-ITEM { string productCode int quantity float pricePerUnit }

LR 声明图表的方向是从左到右,RL可以将其反转为从右到左。

代码:
                            
复制
嵌入
erDiagram direction LR CUSTOMER ||--o{ ORDER : places CUSTOMER { string name string custNumber string sector } ORDER ||--|{ LINE-ITEM : contains ORDER { int orderNumber string deliveryAddress } LINE-ITEM { string productCode int quantity float pricePerUnit }

设置节点样式

可以对节点应用特定样式,例如边框加粗或不同的背景色。

代码:
                            
复制
嵌入
erDiagram id1||--||id2 : label style id1 fill:pink,stroke:yellow,stroke-width:4px style id2 fill:#69d,stroke:#f96,stroke-width:2px,color:#fff,stroke-dasharray: 5 5

也可以在一个语句中将样式附加到节点列表:

                            
复制
嵌入
style nodeId1,nodeId2 styleList

类

比每次定义样式更方便的是定义一个样式类,并将该类附加到需要不同外观的节点上。

类定义如下例所示:

                            
复制
嵌入
classDef className fill:pink,stroke:#333,stroke-width:4px

也可以在一个语句中定义多个类:

                            
复制
嵌入
classDef firstClassName,secondClassName font-size:12pt

将类附加到节点的语法如下:

                            
复制
嵌入
class nodeId1 className

还可以在一条语句中将一个类附加到节点列表:

                            
复制
嵌入
class nodeId1,nodeId2 className

也可以同时附加多个类:

                            
复制
嵌入
class nodeId1,nodeId2 className1,className2

添加类的简便方式是使用运算符 ::: 将类名附加到节点,如下所示:

代码:
                            
复制
嵌入
erDiagram direction TB CAR:::someclass { string registrationNumber string make string model } PERSON:::someclass { string firstName string lastName int age } HOUSE:::someclass classDef someclass fill:#f96

声明实体之间的关系可以使用以下语法:

代码:
                            
复制
嵌入
erDiagram CAR { string registrationNumber string make string model } PERSON { string firstName string lastName int age } PERSON:::foo ||--|| CAR : owns PERSON o{--|| HOUSE:::bar : has classDef foo stroke:#f00 classDef bar stroke:#0f0 classDef foobar stroke:#00f

类似于 class 语句,简写语法也可以一次应用多个类:

                            
复制
嵌入
nodeId:::className1,className2

默认类

如果一个类被命名为 default,它将被分配给所有没有特定类定义的类。

                            
复制
嵌入
classDef default fill:pink,stroke:#333,stroke-width:4px;

注意:来自样式或其他类语句的自定义样式具有优先权,并覆盖默认样式。(例如,default 类为节点设置粉色背景,但如果应用 blue 类,将为该节点提供蓝色背景颜色。)

代码:
                            
复制
嵌入
erDiagram CAR { string registrationNumber string make string model } PERSON { string firstName string lastName int age } PERSON:::foo ||--|| CAR : owns PERSON o{--|| HOUSE:::bar : has classDef default fill:pink,stroke-width:4px classDef foo stroke:#f90 classDef bar stroke:#ff0 classDef foobar stroke:#69f

配置

布局

图表的布局由 render() 处理,默认布局为 ELK 布局,适合较大且更复杂的图表。

要改用经典的 Dagre 布局,你可以在 YAML frontmatter 的 config 中设置它

代码:
                            
复制
嵌入
--- config: layout: dagre ---

Mermaid 代码内容如下:

代码:
                            
复制
嵌入
--- title: Order example config: layout: elk --- erDiagram CUSTOMER ||--o{ ORDER : places ORDER ||--|{ LINE-ITEM : contains CUSTOMER }|..|{ DELIVERY-ADDRESS : uses