4、实体关系图(ERD)实战:用MySQL Workbench绘制第一张业务ER图

说实话,很多做数据的人,画ER图就是拖几个框,连几条线。但你要问他——这张图到底在表达什么业务规则?他往往答不上来。

我见过太多「画得漂亮但没法落地」的ER图了。说白了,ER图不是给数据库看的,是给人看的。它要回答三个问题:业务里有什么?它们怎么关联?约束是什么?

这一章,我们就用MySQL Workbench,从零开始画一张真正能用的业务ER图。我会带着你走完完整流程,顺便聊聊我踩过的坑。

4.1 为什么选MySQL Workbench?

市面上ER图工具很多,PowerDesigner、Navicat、Draw.io……但我个人习惯用MySQL Workbench。原因有三:

  • 免费——团队协作没版权风险
  • 正向工程——画完图直接生成建表SQL,省去手写时间
  • 逆向工程——从现有数据库反向生成ER图,适合存量系统梳理

当然,工具只是手段。你拿什么工具不重要,重要的是你脑子里有没有那张「业务地图」。

小提示:如果你用的是Mac,MySQL Workbench对Retina屏支持一般。我建议在「Preferences → Appearance」里把字体缩放调到120%,眼睛会舒服很多。

4.2 准备业务场景:一个简单的订单系统

为了让你快速上手,我们用一个极简的电商订单场景。别嫌简单——我当年在项目里见过上百张表的ER图,核心逻辑跟这个一模一样。

业务规则如下:

  • 一个客户可以有多个订单
  • 一个订单包含多个订单项
  • 每个订单项对应一个商品
  • 一个商品属于一个分类

嗯,就这么简单。但你要注意——这里已经包含了1对多、多对1两种关系。画ER图的核心就是识别这些关系。

4.3 第一步:创建模型(Model)

打开MySQL Workbench,点击左上角的「Create New EER Model」图标。你会看到一个空白的「MySQL Model」标签页。

我个人习惯先做一件事:改模型名称。默认叫「mydb」,双击改成「order_system」。别小看这一步——项目大了以后,模型一多,命名混乱会让你崩溃。

然后,在「Physical Schemas」面板里,双击「Schema」改名为「order_db」。这就是你将来数据库里的schema名。

关键操作:在「Model Overview」面板里,你可以看到整个模型的结构树。我建议你养成习惯:每加一张表,都先在这里看一眼,确认命名和关系是否正确。

4.4 第二步:添加实体(表)

点击工具栏上的「Add Diagram」图标(一个带加号的方框),进入画布界面。现在,我们来画第一张表。

拖一个「Table」图标到画布上。双击表头,命名为「customer」。然后开始加字段:

customer
├── customer_id (INT, PK, NN, AI)
├── customer_name (VARCHAR(50), NN)
├── email (VARCHAR(100), NN, UQ)
├── phone (VARCHAR(20))
├── created_at (DATETIME, NN, default: CURRENT_TIMESTAMP)
└── updated_at (DATETIME, NN, default: CURRENT_TIMESTAMP ON UPDATE)

注意那几个缩写:PK(主键)、NN(非空)、UQ(唯一)、AI(自增)。这些约束不是随便加的——它们直接对应业务规则。比如email设为UQ,是因为业务上不允许两个客户用同一个邮箱。

我曾经在一个项目里,因为忘了给email加唯一约束,导致线上出现重复客户数据。嗯,那天的加班餐是泡面。

用同样的方法,添加其他三张表:

表名 字段 约束说明
order order_id (PK, NN, AI)
customer_id (FK, NN)
order_date (DATETIME, NN)
total_amount (DECIMAL(10,2), NN)
status (ENUM, NN)
customer_id引用customer表
order_item order_item_id (PK, NN, AI)
order_id (FK, NN)
product_id (FK, NN)
quantity (INT, NN)
unit_price (DECIMAL(10,2), NN)
order_id引用order表
product_id引用product表
product product_id (PK, NN, AI)
category_id (FK, NN)
product_name (VARCHAR(100), NN)
price (DECIMAL(10,2), NN)
stock (INT, NN)
category_id引用category表
category category_id (PK, NN, AI)
category_name (VARCHAR(50), NN)
parent_id (FK, nullable)
parent_id自引用,支持分类层级
注意:MySQL Workbench里,外键不会自动创建。你需要手动在「Foreign Keys」标签页里添加。很多人画完图发现没法生成SQL,就是因为忘了这一步。

4.5 第三步:建立关系(Relationships)

表画完了,现在来连线。工具栏上有个「Relationship」图标(1:1、1:n、n:m三种)。我们这里只用1:n。

点击「1:n Relationship」图标,先点父表(1的一端),再点子表(多的一端)。比如:

  • customer → order:一个客户多个订单
  • order → order_item:一个订单多个订单项
  • product → order_item:一个商品出现在多个订单项中
  • category → product:一个分类下有多个商品

连完线后,双击关系线,在「Foreign Key」标签页里确认:子表的外键字段是否正确?级联删除策略是什么?

我个人习惯:业务核心表用RESTRICT(防止误删),日志或中间表用CASCADE。比如order_item,如果订单被删,订单项应该一起删——用CASCADE。但customer被删时,订单应该保留还是报错?这得看业务。我一般选RESTRICT,让应用层处理。

4.6 第四步:生成ER图与SQL脚本

画完关系后,你的画布上应该有一张完整的ER图。你可以调整布局:选中表拖动,或者用「Arrange → Auto Layout」让Workbench帮你排。

然后,点击「File → Export → Forward Engineer SQL CREATE Script…」。你会看到Workbench生成一段完整的DDL语句:

-- 这是自动生成的,但我会手动检查一遍
CREATE TABLE IF NOT EXISTS `order_db`.`customer` (
  `customer_id` INT NOT NULL AUTO_INCREMENT,
  `customer_name` VARCHAR(50) NOT NULL,
  `email` VARCHAR(100) NOT NULL,
  `phone` VARCHAR(20) NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`customer_id`),
  UNIQUE INDEX `email_UNIQUE` (`email` ASC) VISIBLE
) ENGINE = InnoDB;

-- 其他表类似,省略

注意:自动生成的SQL不一定完美。比如索引、字符集、存储引擎,Workbench默认用InnoDB和utf8mb4,这没问题。但如果你需要分区表或自定义索引,得手动加。

我的习惯:生成SQL后,我会用「Database → Synchronize Model」功能,把模型和实际数据库做一次比对。这能发现字段类型不一致、缺失索引等问题。尤其是多人协作时,这个功能能救你一命。

4.7 本章核心知识图谱

下面这张SVG图,帮你把本章的知识结构串起来。你画ER图时,脑子里应该始终有这张图:

ER图绘制核心流程 1. 业务分析 2. 实体识别 3. 关系定义 4. 字段与约束 5. 外键设置 6. 生成SQL 💡 关键原则: • 先理解业务,再画图——ER图是业务的语言,不是技术的玩具 • 每个约束都要有业务依据——不要为了「规范」而加无意义的约束 • 画完图后,反向验证:用自然语言读一遍关系,看是否通顺

4.8 避坑指南:我踩过的三个坑

画ER图看起来简单,但实际项目中容易翻车。我分享三个真实教训:

  1. 关系线连反了——有一次我把customer和order的1:n连成了n:1,生成SQL后外键加反了。结果测试环境一跑,一个订单关联了多个客户。排查了半天才发现是关系方向错了。所以,连完线后一定要双击检查「Foreign Key」标签页,确认子表是谁。
  2. 忘了加唯一约束——前面说过,email没加UQ导致重复数据。更隐蔽的是:业务上要求「同一订单内商品不能重复」,但我在order_item上没加联合唯一索引(order_id + product_id)。后来补数据时,不得不写脚本去重。
  3. 级联删除设错了——我把category的级联策略设成了CASCADE。结果测试人员删了一个分类,连带删了100多个商品。还好是测试环境。从那以后,我所有业务表的级联策略都默认用RESTRICT,除非业务明确要求。
总结一下:ER图的核心不是「画」,而是「想」。你每画一张表、每连一条线、每设一个约束,都要问自己——这个决策对应什么业务规则?如果答不上来,先别画,回去问业务方。

好了,打开MySQL Workbench,按上面的步骤画一遍。画完以后,把生成的SQL拿到数据库里跑一下,看看能不能建表成功。如果报错,别慌——90%的情况是外键引用顺序不对,调整一下建表顺序就行。

嗯,这一章就到这。记住:ER图是数据模型的骨架,骨架歪了,后面怎么填肉都别扭。