一套数据库能不能长期维护,往往在写第一条 SQL 之前就已经决定了一半。我们需要先回答三个问题:业务里有哪些可以独立识别的对象,对象之间发生什么联系,哪些规则无论应用怎样改版都必须成立。实体关系模型(E-R 模型)就是把这些答案整理成概念模式的方法。
下面统一设计一个“城市研学活动平台”。平台发布活动,活动可以开设多个场次;参与者报名场次;工作人员在场次中承担讲解、签到或安全巡查等岗位;场地、活动分类和评价也要被记录。这个场景足够小,便于看清符号,又包含弱实体、多值属性、递归联系、三元联系、特化与聚合等现实问题。

图:城市研学活动平台从需求访谈、概念建模到关系实现与物理优化的完整路径。
数据库设计不是“先列几张表,再看缺什么字段”。真实项目里,产品人员知道流程,运营人员知道例外,财务人员知道结算规则,开发人员知道接口,但通常没有一个人掌握全部数据需求。设计者要把分散在不同角色脑中的规则收集起来,用业务人员能核对的形式表达,再逐层变成数据库系统可以执行的结构。
需求分析要同时记录数据需求与功能需求。数据需求说明要保存什么;功能需求说明这些数据会怎样被查询、更新和删除。对研学平台,我们至少要问清这些问题:
问题要落成可验证的陈述。例如,“每个场次必须从属于且只从属于一个活动”“一名参与者在同一场次最多有一份有效报名”“场次尚未排定时可以没有场地”。这些句子以后会分别变成存在依赖、唯一约束、最小基数和可空性。
需求文档里最有价值的内容通常不是名词清单,而是带有“每个”“最多”“至少”“只有……才能”“删除时”等词的业务规则。它们直接决定键、基数、参与约束与级联行为。
概念设计用实体、属性、联系和约束描述业务语义。此时我们讨论“参与者报名场次”,还不急着决定外键放在哪张表。概念模式应让领域人员看得懂,也应覆盖已知查询和事务。例如,运营要查询某场次的候补名单,概念模式就必须能区分有效报名、候补与取消状态。
逻辑设计把概念模式映射到目标数据库支持的数据模型。使用关系数据库时,这一步会得到表、列、主键、外键、唯一约束和检查约束。参与者、活动、场次与报名会变成关系模式,但并非每个 E-R 图形都机械地对应一张最终表;某些联系表可以合并,某些多值属性必须拆表。
物理设计才处理索引、文件组织、分区、聚簇方式和访问路径。比如报名查询经常按 场次编号 + 报名状态 过滤,可以据此设计复合索引。物理结构通常能在不改变业务语义的情况下调整,而逻辑结构一旦被接口、报表和应用代码依赖,修改成本会高得多。
先访谈角色并整理术语表,明确“活动”“场次”“报名”“岗位安排”等词在当前业务中的唯一含义。
再写出对象、联系和约束的概念模式,用样例数据与反例让业务人员核对。例如用“尚未开设场次的新活动”验证模式能否表达未来计划。
接着检查功能需求。用“发布活动、排场次、报名、取消、换场地、生成签到名单”逐项走查,确认每个操作都有数据落点。
然后映射成关系模式,补齐主键、外键、唯一性、非空和必要的检查约束,并决定哪些联系表可以安全合并。
最后根据实际查询与更新负载做物理设计。索引和分区服务于访问路径,不应反过来扭曲概念模式。
第一类问题是冗余。如果每个场次都重复保存活动标题、活动分类名称和主办方名称,那么一次标题修改要更新很多行。漏掉任何一行,同一活动就会出现多个标题。更稳妥的做法是让活动信息只由活动实体维护,场次只记录与活动的联系。
第二类问题是不完整。如果只建“场次”而没有“活动”,平台就无法提前登记一个尚未排期的新活动。为了保存它而伪造日期、空场次或临时编号,会让主键和业务含义都变得混乱。一个模式即使没有重复,也可能因为不能表达合法业务状态而失败。
E-R 模型的基本词汇只有三组:实体集表示同类对象,属性描述对象或联系,联系集表示对象之间的关联。难点不在记住定义,而在于判断某个业务概念应该放在哪一组。

图:实体用矩形、联系用菱形、主键属性加下划线;同一实体集重复参与联系时必须标明角色。
实体是能够与其他对象区分开的具体或抽象对象。某位参与者是实体,一项活动是实体,一次具体场次也是实体。场地是具体对象,活动分类则比较抽象,但只要它有独立身份、属性或联系,同样可以建成实体。
实体集是结构相同的一类实体,例如全部参与者组成“参与者”实体集。实体集描述类型,某一时刻真正存在的参与者集合是这个实体集的当前外延。模式中的属性相对稳定,外延会随着注册和注销不断变化。
不同实体集不必互斥。平台中的一个人可能既是参与者又是工作人员,也可能暂时两者都不是。是否允许重叠不能凭名称猜,要写成特化约束。
每个实体由一组属性描述。参与者编号用于识别身份,姓名用于展示,出生日期用于年龄计算。名字通常不适合作为键,因为同名很常见;身份证件号码又可能带来隐私与跨系统变更问题。业务系统通常应生成自己的稳定标识符。
联系是若干实体之间的一次关联,联系集是一类同型联系的集合。例如,“参与者 P018 报名了活动 E102 的第 3 场”是一条报名联系实例;所有这类实例组成“报名”联系集。形式上,若实体集为 ,联系集 是笛卡尔积的子集:
实体集出现在某个联系集中,称为参与这个联系。二元联系连接两个实体集,三元联系连接三个实体集,联系的度就是参与实体集的位置数。大多数联系是二元的,但不能因为二元图更好画,就把本来不可分割的三元事实拆错。
例如“值班安排”可以同时连接工作人员、场次和岗位。它表达“哪位工作人员在某场次承担哪个岗位”。若拆成“工作人员—场次”和“工作人员—岗位”两条联系,只能知道某人参加过哪些场次、做过哪些岗位,却无法还原某个岗位对应哪一场。
同一实体集可以多次参与一个联系。活动之间的“前置活动”就是递归联系:两个参与位置都来自“活动”,但一个角色是“后续活动”,另一个是“前置活动”。有序对 (E201, E101) 和 (E101, E201) 含义不同,所以图上应在两条边分别标注角色名。
角色名也会影响关系映射。同一个主键从两个位置进入关系表时,不能都叫 活动编号,应改为 后续活动编号 与 前置活动编号,否则列无法区分。
有些数据属于关联,而不属于任何一端。报名时间描述“这个人报名这个场次”的事实,放在参与者或场次上都不合适,因此它是“报名”的描述性属性。同理,报名状态、签到时间也可以属于联系。
联系集在数学上是集合,所以同一组参与实体默认最多形成一条联系实例。若平台要求保存同一参与者对同一场次的多次报名与取消历史,单纯给“报名”联系增加 发生时间 并不能改变身份规则。更自然的做法是把每次报名提升为带独立编号的“报名记录”实体,再让它连接参与者与场次。
参与实体的主键在联系中是隐含的,不要又把它们画成联系的普通属性。映射到关系表时这些键会成为列,但概念图里重复绘制只会制造两份含义相同的信息。
属性不是一律对应一列。设计时要先确认允许值的集合,也就是属性的域,再判断属性是简单还是复合、单值还是多值、存储还是派生。不同判断会产生完全不同的关系结构。

图:参与者姓名与地址逐层分解,联系电话是多值属性,年龄由出生日期派生。
简单属性在当前业务粒度下不再拆分,例如 参与者编号。复合属性由多个成分组成,例如“姓名”可拆成姓与名,“联系地址”可拆成省、市、区、街道和门牌。复合属性还可以继续嵌套,街道地址又可拆成道路、门牌号和房间号。
是否拆分取决于访问需求。如果平台只打印完整收件地址,一个文本值可能足够;如果要按城市统计参与者或检查邮编,必须保留相应成分。不要为了“规范”无限拆分,也不要把未来确定要筛选的结构塞进不可解释的长字符串。
单值属性对一个实体最多有一个值,多值属性则允许一个值集合。参与者可能登记零到多个联系电话,工作人员也可能拥有多个资格证书。多值属性应写出允许的下界与上界,例如 联系电话 0..3,而不是只写“可多个”。
关系数据库的一行一列应保存一个原子值,因此多值属性通常会映射成独立关系。例如 参与者电话(参与者编号, 电话号码),一名参与者的三个号码对应三行。把多个号码用逗号拼成一个文本,会让唯一性、格式校验和号码查询都变困难。
派生属性可由其他数据计算得到。年龄来自 出生日期 与当前日期,场次已报名人数来自有效报名的计数,剩余名额来自容量减去有效报名数。派生值默认不必存储,否则基础数据更新时还要同步维护副本。
并非所有可计算值都绝不能存。高频聚合可能为了性能做缓存,但这已经是带一致性维护策略的物理优化。概念图仍应标明它是派生值,避免团队误以为它是独立事实。
空值可能表示三种不同情况:属性对该实体不适用;值存在但暂时缺失;甚至不知道这个值是否存在。比如“房间号为空”可能是独栋地址没有房间号,也可能是用户没填。两种情况在补全资料时处理方式不同。
如果业务必须区分这些状态,可以增加状态属性或改造结构,而不能让一个 NULL 同时承担全部含义。主键、鉴别属性和强制参与联系所需的键值更不能依赖含糊的空值。
实体和联系说明“有什么”,约束说明“什么状态才合法”。同一张没有约束的图,既可能表示每人一个负责人,也可能表示多人联合负责。基数、参与约束和键必须一起阅读。

图:四类映射基数、全参与与部分参与,以及 0..*、1..1 等最小—最大标记。
对实体集 与 之间的二元联系,最大基数有四种:
“一对多”和“多对一”只是阅读方向不同。说“活动到场次是一对多”,等价于说“场次到活动是多对一”。画箭头的记法中,箭头指向“最多一个”的实体集;无箭头的一侧表示可能有多个。不要把箭头理解成业务流程方向。
最大基数回答“最多几个”,参与约束回答“最少几个”。每个场次都必须属于某个活动,因此场次在“从属”活动联系中是全参与;活动可以先创建、以后再排场次,因此活动是部分参与。
全参与常用双线表示。更精确的最小—最大记法写成 l..h:1..1 表示恰好一个,0..* 表示可以没有也可以任意多个,1..* 表示至少一个。研学平台的“活动—场次”可以读成:每个场次对应 1..1 个活动,每个活动对应 0..* 个场次。
一个常见误读是把边旁的 0..* 当成“另一端有多个”。在本页采用的 E-R 记法中,某实体集旁的范围描述该实体自身参与联系的次数;使用 UML 时标注位置又可能相反。团队必须先固定图例,再解释数字。
超键是能唯一识别实体的任意属性集合;候选键是没有多余属性的最小超键;主键是从候选键中选定的主要标识。参与者编号可作为主键,参与者编号 + 姓名虽也唯一,却只是含多余属性的超键。
候选键应同时满足唯一、稳定和尽量简洁。手机号会被更换,也可能被多人共享,不适合直接承担参与者主键。主键属性在 E-R 图中通常用下划线标出。
联系实例由参与的实体组合识别。对多对多“报名”联系,主键通常是 参与者编号 + 场次主键。对从场次到活动的多对一联系,场次一侧的主键已经足以识别联系,因为一个场次最多指向一个活动。对一对一联系,任一侧主键都可形成候选键,最终选择要考虑外键方向和参与约束。
关系的描述性属性不应用来区分原本相同的一组参与实体。如果同一参与者和场次之间确实需要多次独立发生的报名,就应把“报名记录”建成实体,或重新定义联系参与者,而不是悄悄把时间戳塞进联系主键。
对三元联系,若没有额外基数约束,三个参与实体的主键并集是自然候选键。若从“工作人员 + 场次”可确定至多一个岗位,可以在指向岗位的位置表达多对一约束,主键可缩为工作人员键与场次键的并集。非二元联系上同时画多支箭头容易产生两种不同的函数依赖解释,最好改用明确约束或提升为实体。
有些对象离开所属对象就没有完整身份。活动场次常用“某活动在某天的第几场”编号:第 1 场在不同活动、不同日期里都会重复,只有连同活动编号和活动日期才唯一。这正是弱实体适合表达的存在依赖。

图:活动是标识实体,活动场次是弱实体;双框、双菱形、双线与虚线下划线分别表达弱实体、标识联系、全参与和鉴别属性。
弱实体集没有足够的自身属性形成主键,它依赖一个或多个标识实体集。弱实体自身用于区分同一所有者下各成员的属性叫鉴别属性或部分键。
在平台中,场次序号每天重新从 1 开始,因此活动场次的鉴别属性是 活动日期 + 场次序号。这组属性只保证同一个活动内部不重复,完整主键为:
如果另一个平台规定场次序号在活动内永不重复,鉴别属性才可以缩为 场次序号。鉴别属性必须根据真实唯一规则确定,不能照搬示例。
标识联系从弱实体到所有者必然是多对一,弱实体一侧必然全参与:每个场次必须属于一个活动,不能孤立存在。标识联系不应再带描述性属性,因为这些属性可以直接放在弱实体上。弱实体还可以参加普通联系,也可以继续标识更低层的弱实体。
即使系统给场次生成全局唯一的 UUID,概念上它仍可能存在依赖活动。是否建成弱实体不只取决于“有没有技术主键”,还取决于业务身份和生命周期是否离不开所有者。
初画实体时,人们常把熟悉的表字段全部写进去。例如在“活动场次”中先写 活动编号,又画一条从场次到活动的“从属”联系;在“场次”中写 场地编号,又画“安排场地”联系。这样同一事实出现了两次。
概念模型应保留联系,删除作为外键影子的普通属性。原因有两个:一是联系把语义与基数写得更清楚;二是过早放入外键会偷偷假设“最多一个”。如果未来一个场次能使用多个相邻场地,联系只需调整基数,而单个 场地编号 属性从一开始就表达错了。
映射到关系模式后,这些键可能重新作为外键列出现在表中。它们在逻辑表里出现,并不意味着概念实体也应重复画一遍。概念图表达业务联系,关系表用外键实现联系,两层的外观不能混为一谈。
映射不是“矩形变表、菱形也变表”这么简单。强实体、复合属性、多值属性、弱实体和不同基数的联系各有规则,最后还要消除标识联系产生的重复关系,并在满足条件时合并模式。

图:从强实体、复杂属性、弱实体和联系集出发,逐步得到主键、外键与可合并的关系模式。
只有简单属性的强实体直接变成同名关系,实体主键成为关系主键:
活动(活动编号, 标题, 简介, 容量上限)
参与者(参与者编号, 姓, 名, 出生日期)
场地(场地编号, 场地名称, 地址, 容量)复合属性只保留最末级成分,不为中间复合名额外建列。若姓名由姓、名组成,关系中保存 姓 和 名,不再同时保存一个可独立修改的 姓名。
多值属性单独建关系,并带上所属实体的主键。参与者的联系电话变成:
参与者电话(参与者编号, 电话号码)
主键:(参与者编号, 电话号码)
外键:参与者编号 → 参与者(参与者编号)每个电话号码占一行,整个属性集合共同作为主键。派生属性如年龄与已报名人数默认不进入基础关系,可以由查询、视图或函数计算。
有一种谨慎使用的优化:如果某实体只有一个主键属性和一个多值属性,实体关系可能只剩主键列,可以只保留多值属性关系。但这样可能失去“当前没有任何多值项的实体”以及被其他外键稳定引用的目标,实际设计时要先检查语义与参照完整性。
弱实体关系包含自身属性、所有标识实体的主键。主键由所有者键与鉴别属性组成,并建立指向所有者关系的外键:
活动场次(
活动编号,
活动日期,
场次序号,
开始时间,
结束时间,
容量
)
主键:(活动编号, 活动日期, 场次序号)
外键:活动编号 → 活动(活动编号)删除活动时如何处理场次是生命周期规则。若场次绝不允许脱离活动,可以采用级联删除;若历史订单必须保留,则应禁止物理删除活动,改用停用状态或归档策略。外键能保证存在性,但不能替代业务对历史的要求。
联系关系包含所有参与实体的主键,加上联系自己的描述性属性。重复列名要使用实体名或角色名区分。多对多报名联系可映射为:
报名(
参与者编号,
活动编号,
活动日期,
场次序号,
报名时间,
状态,
签到时间
)
主键:(参与者编号, 活动编号, 活动日期, 场次序号)
外键:参与者编号 → 参与者
外键:(活动编号, 活动日期, 场次序号) → 活动场次递归“前置活动”联系使用角色名:
前置活动(后续活动编号, 前置活动编号)
主键:(后续活动编号, 前置活动编号)
两个属性分别外键引用 活动(活动编号)三元“值班安排”若无额外函数约束,关系包含工作人员、场次与岗位三方主键,三者并集为主键。若已确认“工作人员 + 场次”至多确定一个岗位,则可把前两者作为主键,并对岗位保留外键。
弱实体与所有者之间的标识联系通常没有描述性属性。映射后,“场次从属活动”关系只会得到 活动编号 + 场次鉴别属性,而这些列已经完整存在于 活动场次 关系中,所以标识联系关系是重复的,应删除。
这个删除有严格前提:它是标识联系、从弱实体到所有者多对一、弱实体全参与且联系没有额外属性。普通联系即使列看起来相似,也不能直接删。
对从 到 的多对一联系,如果 全参与,可以把联系关系并入 ,将 的主键作为 的非空外键。场次必须且只使用一个场地时,可把 场地编号 放入 活动场次。若场次允许暂未排场地,仍可合并,但外键需要允许空值;是否接受这种空值要结合业务语义判断。
一对一联系可以并入任一侧,通常选择全参与的一侧,以减少空值。多对多联系通常必须保留独立关系。只要联系有需要独立保留的多次事件、复杂生命周期或被其他对象引用,把它提升为实体往往比强行合并更清楚。
基本的实体、联系和属性可以描述大多数数据,但遇到“同一类对象中的不同子类”或“某个联系还要参与另一个联系”时,扩展 E-R 能更准确地保留语义。

图:人员到参与者和工作人员的重叠特化,工作人员的进一步特化,以及把三元指导联系聚合后连接评价。
平台先有“人员”实体,保存人员编号、姓名和联系方式。部分人员是参与者,拥有累计学时;部分人员是工作人员,拥有入职日期和薪酬等级。把一个高层实体集划分成更具体的低层实体集叫特化,是自上而下的设计过程。
低层实体继承高层实体的属性,也继承高层实体参与的联系。若人员与紧急联系人有联系,参与者和工作人员自然都能参与这条联系。低层实体还可以增加专属属性与联系,例如讲师有擅长主题,安全员有资格证。
特化可以继续多层展开。工作人员再分成讲师和运营人员。若每个低层实体只直接继承一个高层实体,结构是层次;若某个低层实体同时来自多个高层实体,就形成多重继承的格结构,实现映射会更复杂。
如果设计一开始分别发现“讲师”和“运营人员”,后来看到它们都有人员编号、姓名、入职日期和薪酬等级,就可以把共同部分抽成“工作人员”。这种自下而上的过程叫概化。
特化强调差异,概化强调共性,但完成后的 ISA 结构相同。没有专属属性、专属联系或不同约束的子类通常没有保留价值,只会增加连接与维护成本。
不相交特化要求一个高层实体最多属于一个直接子类;重叠特化允许同时属于多个。一个人可以既是参与者又是工作人员,所以这组特化是重叠的。若平台规定工作人员在同一时期只能是讲师或运营人员之一,这组特化是不相交的。
完全特化要求每个高层实体至少属于一个子类;部分特化允许高层实体暂时不属于任何子类。两组约束彼此独立,因此有“完全且重叠”“完全且不相交”“部分且重叠”“部分且不相交”四种组合。
完全约束会影响插入:新增人员时必须同时插入至少一个子类。不相交约束会影响更新:不能让同一工作人员同时出现在两个互斥子类中。图形只是说明,数据库实现还需要唯一约束、断言、触发器或应用事务来落实。
假设“现场指导”是讲师、参与者和场次之间的三元联系。平台还要记录一份评价,评价针对的是“某讲师在某场次指导某参与者”这一完整事实,而不是只针对讲师、参与者或场次中的任意一个。
若直接建立四元联系,会重复说明每条评价对应的三元组合一定先存在。聚合的做法是把“现场指导”连同参与实体看成一个高层整体,再建立“评价针对现场指导”的联系。这样可以表达:现场指导可以暂时没有评价,但每条评价必须指向一个真实的指导组合。
特化/概化最稳妥的映射是“高层一张表,每个低层各一张表”:
人员(人员编号, 姓名, ...)
参与者(人员编号, 累计学时)
工作人员(人员编号, 入职日期, 薪酬等级)子类主键同时是外键,引用高层关系。这个方案支持重叠与部分特化,也提供统一的外键目标。
只有在特化同时满足“完全且不相交”时,才可能省略高层表,把高层属性复制到各子类表。即便如此,其他关系若要统一引用“人员”,就没有单一外键目标;未来若变成重叠特化,共同属性还会重复。因此工程上常保留高层表。
聚合不需要额外创建一张只表示“聚合框”的关系。定义该聚合的联系关系已经承载它的主键;涉及聚合的新联系,引用这组主键即可。
E-R 建模没有一条规则能仅凭名词决定对象类型。同一个“联系电话”,在简单通讯录里可以是多值属性;在需要记录设备类型、归属地点、共享人员和停用历史的系统里,更适合成为实体。判断标准是业务语义,而不是图形是否省事。

图:同一需求的多种建模方案及其能表达、不能表达的业务状态。
适合保留为属性的概念通常没有独立生命周期,不会被多个对象共享,也没有自己的属性和联系。姓名就是典型例子。适合提升为实体的概念往往满足以下一项或多项:
多值并不自动意味着实体。若只需要保存若干号码,参与者电话子关系已经足够;增加“电话实体”会引入没有实际价值的标识符和连接。
“参与者报名场次”用联系表达很紧凑,前提是每对参与者和场次最多一条报名,联系属性也较简单。若报名有独立编号、支付、退款、票券、审核日志和状态流转,还会被评价与发票引用,那么“报名记录”应提升为实体,并分别连接参与者和场次。
一个实用判断是:它是否只是实体之间发生的一次动作,还是已经成为会被单独查询、引用和管理的业务对象。前者适合联系,后者适合关联实体。
有些三元联系可以安全拆成二元联系。例如孩子与父亲、孩子与母亲是两种角色明确且彼此独立的事实,拆开后还能在父亲未知时保留母亲信息。
但“工作人员在某场次承担某岗位”不能直接拆成三条两两联系,否则会产生虚假组合。假设某人参加了场次 A 和 B,也承担过讲解与签到岗位,两条二元联系无法说明 A 对应讲解、B 对应签到。
任意 元联系都能通过新建一个关联实体,再建立 条从该实体到原实体的多对一联系来表示。关联实体应全参与这些联系,原联系的属性也移到关联实体上。代价是需要额外标识符、更多关系和连接,而且“若干实体共同参与一次事实”的直观性会减弱。
更重要的是,原三元联系上的某些约束无法只靠新二元边的基数表达。例如“工作人员 + 场次至多确定一个岗位”是组合键约束,必须在关联实体关系上用唯一约束明确实现。
设计取舍可以用反例验证:尝试放入“零个、多个、重复发生、暂时未知、跨对象共享、规则变化”这些边界状态。哪种结构能准确表达合法状态、拒绝非法状态,哪种结构才更接近业务。
E-R 图没有唯一的全球统一画法。不同工具可能用属性框、椭圆、乌鸦脚或最小—最大范围表达同一语义。设计文档必须附图例,评审时先统一符号含义,再讨论业务。

图:同一组实体、联系、基数、弱实体与 ISA 结构在常见记法中的对应方式。
本页采用的紧凑记法把实体画成分区矩形,上部是实体名,下部是属性;主键加实线下划线,弱实体鉴别属性加虚线下划线。联系用菱形,标识联系用双菱形,弱实体用双矩形,全参与用双线,箭头指向“至多一个”的一侧。
另一类经典记法把属性画成连接实体的椭圆:双椭圆表示多值属性,虚线椭圆表示派生属性,复合属性继续连接成分椭圆。它展开后直观,但大型模式占用空间较多。
乌鸦脚记法常省略联系菱形,直接用线连接两个实体。乌鸦脚表示“多”,竖线表示“一个”,空心圆表示“可选”。要小心竖线或圆的位置通常放在被约束实体的相对端,不能机械套用另一种图的阅读规则。
UML 类图也用分区矩形,但类除了属性还可以包含方法。属性或方法前的 +、-、# 分别表示公开、私有和受保护。E-R 模型关注数据库中的实体与联系,UML 类图还服务于整个软件对象设计,两者目标不完全相同。
UML 把二元联系称为关联,通常用一条线表示;角色名写在线端,关联自身有属性时可使用关联类。较新的 UML 也能用菱形表示多元关联。UML 的多重性仍写成 0..1、1..* 等,但标注位置与本页 E-R 记法相反:某一端旁的数字通常描述对面一个对象能关联这一端多少对象。
UML 没有直接等同于 E-R 复合属性和多值属性的通用表示;派生属性常可用无参数方法表示。UML 的实心菱形“组合”大致表达部件对整体的存在依赖,可类比弱实体,但两者的生命周期语义与实现规则不能完全画等号。空心菱形在 UML 中叫聚合,也不是扩展 E-R 中“把联系提升为高层对象”的聚合概念。
在工具之间迁移图时,应逐项核对:主键、鉴别属性、联系角色、最小与最大基数、全参与、特化的重叠/不相交和完全/部分。只要这些语义不丢,矩形、椭圆或乌鸦脚的外观变化并不影响模式;反之,画得相似也可能约束完全相反。
概念模式只解决数据结构的一部分问题。一个能投入使用的数据库应用还要考虑事务功能、界面、授权、跨系统数据流、工作流和模式演化。它们会反过来检验 E-R 设计是否真的支撑业务。
把核心操作列成事务清单:创建活动、开设场次、安排场地、分配岗位、报名、候补转正、取消、签到、退款和评价。对每个事务写出前置条件、读取的数据、修改的数据和失败后的状态。
例如“候补转正”要同时检查名额、修改报名状态并生成通知。如果概念模式只保存一个不可追踪的报名人数,就无法找到具体候补者;如果把剩余名额和有效报名都独立存储,又没有原子更新规则,就可能出现两个互相矛盾的值。
界面也会暴露结构问题。运营需要在一个页面看到活动、各场次、场地和负责人,这要求联系能高效连接;参与者只应看到自己的报名和公开场次,不应因为一个方便的宽表而获得其他人的手机号。
授权既可以由数据库角色、视图和行级策略实现,也可以由应用功能控制。设计时至少要区分公开活动信息、参与者个人信息、工作人员排班、支付数据和审计日志。敏感标识不要为了方便关联而四处复制,统一内部主键能缩小泄露面。
授权规则同样需要考虑关系语义。例如工作人员只能查看自己负责场次的签到名单,这不是单表字段权限,而是沿“工作人员—值班安排—场次—报名”联系判断的行级条件。
研学活动的退款可能经历参与者申请、运营审核、财务支付和通知完成。工作流不仅使用报名与支付数据,还要记录流程实例、当前任务、处理人、时间和流转结果。若只保存最终退款状态,就无法解释谁在何时批准,也无法恢复中断流程。
跨系统数据流也要明确数据所有权。支付平台返回交易结果,消息系统发送通知,数据库要记录业务需要的引用和结果,但不应无目的复制外部系统全部字段。哪些是主数据、哪些是事件快照、哪些可重放,应在逻辑设计中写清楚。
“参与者编号唯一”接近永久身份约束;“每个场次只能有一名负责人”可能只是当前运营政策。若后者将来很可能变成联合负责,把负责人直接做成场次的单值属性会造成较大改造,保留显式联系更容易调整基数。
好的设计不需要猜中所有未来,但应识别已知会变化的政策。记录关键设计决定:当前基数是什么、为什么这样定、哪条规则可能变化、变化时会影响哪些表和接口。模式演化还要配合迁移脚本、兼容视图、数据回填与应用发布顺序,不能只改一张图。
| 多个 |
| 从场次看活动,是多个场次指向一个活动 |
| 多对多 | 多个 | 多个 | 参与者可报名多个场次,一个场次可有多名参与者 |