4、实体关系图(ERD)实战:用MySQL Workbench绘制第一张业务ER图
说实话,很多做数据的人,画ER图就是拖几个框,连几条线。但你要问他——这张图到底在表达什么业务规则?他往往答不上来。
我见过太多「画得漂亮但没法落地」的ER图了。说白了,ER图不是给数据库看的,是给人看的。它要回答三个问题:业务里有什么?它们怎么关联?约束是什么?
这一章,我们就用MySQL Workbench,从零开始画一张真正能用的业务ER图。我会带着你走完完整流程,顺便聊聊我踩过的坑。
4.1 为什么选MySQL Workbench?
市面上ER图工具很多,PowerDesigner、Navicat、Draw.io……但我个人习惯用MySQL Workbench。原因有三:
- 免费——团队协作没版权风险
- 正向工程——画完图直接生成建表SQL,省去手写时间
- 逆向工程——从现有数据库反向生成ER图,适合存量系统梳理
当然,工具只是手段。你拿什么工具不重要,重要的是你脑子里有没有那张「业务地图」。
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名。
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自引用,支持分类层级 |
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,这没问题。但如果你需要分区表或自定义索引,得手动加。
4.7 本章核心知识图谱
下面这张SVG图,帮你把本章的知识结构串起来。你画ER图时,脑子里应该始终有这张图:
4.8 避坑指南:我踩过的三个坑
画ER图看起来简单,但实际项目中容易翻车。我分享三个真实教训:
- 关系线连反了——有一次我把customer和order的1:n连成了n:1,生成SQL后外键加反了。结果测试环境一跑,一个订单关联了多个客户。排查了半天才发现是关系方向错了。所以,连完线后一定要双击检查「Foreign Key」标签页,确认子表是谁。
- 忘了加唯一约束——前面说过,email没加UQ导致重复数据。更隐蔽的是:业务上要求「同一订单内商品不能重复」,但我在order_item上没加联合唯一索引(order_id + product_id)。后来补数据时,不得不写脚本去重。
- 级联删除设错了——我把category的级联策略设成了CASCADE。结果测试人员删了一个分类,连带删了100多个商品。还好是测试环境。从那以后,我所有业务表的级联策略都默认用RESTRICT,除非业务明确要求。
好了,打开MySQL Workbench,按上面的步骤画一遍。画完以后,把生成的SQL拿到数据库里跑一下,看看能不能建表成功。如果报错,别慌——90%的情况是外键引用顺序不对,调整一下建表顺序就行。
嗯,这一章就到这。记住:ER图是数据模型的骨架,骨架歪了,后面怎么填肉都别扭。