Insights·2026-09-29

ERD 里放成列还是拆成表 — 会出现多个就拆成表

一个对象上某个值只有一个就放成列,可能有多个就拆成单独的表。以手机请柬为例,标题、日期、地点是请柬的列,宾客留下的祝福语会有很多条,于是成为单独的表。这种一对多(1:N)关系最常见,ERD 用连线末端的竖线、圆圈和三叉符号来表示。像直播链接这样要么没有、要么只有一个的值,放成列就够了;就连模板 1、2、3 这种事先定好的值,一旦需要不断增删,也会升级为表。

값이 하나면 컬럼, 여러 개 붙으면 테이블 — 글의 요약 도식

判断标准只有一个 — 会不会出现多个

ERD 是画出一个服务管理哪些东西、这些东西彼此如何相连的图。它是 Entity Relationship Diagram 的缩写,一个管理对象(实体)在数据库里就成为一张像 Excel 表格一样的表。表里竖着的一格是列,横着的一行是一条数据。

课程用手机请柬服务来讲解。管理员登录后制作请柬。这里的管理对象有两个:管理员和请柬。请柬里的标题、日期、地点、留言、新郎姓名、新娘姓名不是需要单独管理的对象,而是请柬的属性,所以成为请柬表的列。

服务运转顺利,于是再加一个功能:让宾客留下「恭喜」之类的祝福语。祝福语要能新写、修改和删除。能不能把它作为列挂在请柬表上?

如果一张请柬只会有一条祝福语,挂上去也可以,没必要另建一张表。但一场婚礼会有很多宾客留言。一个格子只能放一个值,所以这种做法行不通。于是把祝福语拆成单独的表,再用连线和请柬相连。

课程强调的正是这种感觉:某个值只存在一个就是列,可能出现多个就是表。每次加功能时,先问这个值在一个对象上会挂几个。

祝福语只有一条 vs 有多条
[只有一条] invitations 表加一列
id  title        greeting
1   我们结婚了     恭喜

[有多条] 单独的 greetings 表
id  invitation_id  content
1   1              恭喜
2   1              祝你们幸福
3   1              新婚快乐

1:N、父与子 — 读懂连线末端的符号

表与表之间的关系大多是 1:N,意思是一个对应多个。一位管理员制作多张请柬,一张请柬收到多条祝福语,两者都是 1:N。

一的一方叫父,多的一方叫子。请柬只认创建它的那一位管理员,所以管理员是父,请柬是子。子表里放一列记录父的 id。请柬表里的 admin_id、上面例子中祝福语表里的 invitation_id,就是这样的格子。这种指向另一张表某一行的列叫外键。

在 ERD 里,给有关系的两张表之间画一条线,用线末端的形状表示数量。一的一方是竖线,多的一方是线末端分成三叉的形状。它像鸟爪,所以叫鸦爪表示法。

是否必须存在也标在同一位置。必须有就是竖线,可以没有就是圆圈。管理员可能一张请柬都还没做,所以请柬一端是圆圈和三叉叠在一起。反过来,请柬没有管理员就不会产生,所以父的一端通常是两条竖线。

线末端的两个符号中,靠近表的那个表示最大数量,离表远的那个(靠线中间)表示最小数量。只要读懂 AI 画出的 ERD 上的这些符号,就能看出服务允许什么。

符号位置含义
竖线 |靠近表的一侧最多一个
三叉 {靠近表的一侧多个
竖线 |离表远的一侧至少一个(必需)
圆圈 o离表远的一侧可以没有(可选)
用符号表示请柬服务的关系
管理员 ||--o{ 请柬      一位管理员对应 0 张以上请柬
请柬   ||--o{ 祝福语    一张请柬对应 0 条以上祝福语
请柬   ||--o| 直播      一张请柬对应 0 个或 1 个链接

只有一个却单独拆出来 — 可选的 1:1

这次是直播功能。它是为不能亲自到场的宾客直播婚礼的付费服务,所以有人申请,也有人不申请。一场婚礼的直播链接要么没有,要么只有一个,不需要多个。

这种关系叫 1:1,而且因为可能没有,所以叫可选的 1:1。如果把直播拆成单独的表,请柬一端画两条竖线,直播一端画圆圈和竖线。

不过课程说其实没必要这样做。在请柬表上加一个链接列,只给申请的人填上就行。这个值最多只有一个,按前面的标准就是列。

需要拆出来的情况,是这个格子空着的次数太多。如果十对新人里只有一对申请,其余九行的这一格都是空的。课程解释说,空格这么多、想管理得更有效率时,也可以拆成 1:1。不过这不是主流用法。1:1 只当作一个选项记住,默认还是列。

默认放成列,大多为空时再拆
[默认] invitations 表加一列
id  title        live_stream_url
1   我们结婚了     (空)
2   春日之约       https://live.example.com/abc

[可选] 单独的 live_streams 表
id  invitation_id  url
1   2              https://live.example.com/abc

原本是 enum 的模板变成表的那一刻

最初的设计里,模板定为 1、2、3 三种。这种只接受事先定好的值、其他值一进来就报错的数据类型叫 enum。这和把登录方式限定为 Kakao、Google、Apple 是同一种做法。

可是模板设计不够好,于是新招了设计师,想不断增加模板。一开始是改代码加上 4 号,再加 5 号,再去掉 1 号。这种事一再发生,每次都要动代码或数据库就变得很麻烦。

这时干脆把模板做成一张表。每个模板有自己的 id(使用 UUID,一种为了不重复而生成的 ID 字符串),请柬不再用 enum 列,而是有一列 template_id。template_id 指向模板表的 id。一个模板可以做出多张请柬,所以模板和请柬是 1:N,模板是父。

课程留下的启示是:即使是定成几个值的东西,以后一旦需要管理,也可以从列拆成表。如果增删变得频繁,那个值就不再是属性,而是成了管理对象。不过,把已经存下的请柬上的 1、2、3 迁移到新 id 的工作需要小心,这会在第 04 篇讲。

今天可以做的事:让 AI 整理出当前项目的表结构,再用下面的请求逐项检查。下一篇看多位管理员共同管理一张请柬的多对多关系如何用中间表来解决,以及如何用 Mermaid(把写成代码的文字变成图的工具)让 AI 画出 ERD。

从 enum 列到模板表(id 为简写)
[之前] invitations — template 是 enum
id  title        template
1   我们结婚了     template_1
2   春日之约       template_2

[之后] templates 表 + template_id
templates
id    name
t-01  经典
t-02  花卉

invitations
id  title        template_id
1   我们结婚了     t-01
2   春日之约       t-02
给 AI 的请求
把这个项目的数据库表和列整理成表格。
然后找出下面三类:
1. 把可能有多个的值塞进一列的地方
   (例如 greeting1、greeting2 这种带编号的列,或用逗号连在一起的值)
2. 大多数行都为空的列
3. 事先定好的值(enum)中,今后管理员需要增删的
逐一告诉我应该拆成表还是可以保留为列,并说明理由。
先不要改任何东西。