简介:一份讲解数据库系统概念建模的PPT课件,围绕实体-联系(ER)模型展开,面向数据库初学者、高校学生及需要快速梳理概念设计知识的备考者。内容系统覆盖信息世界基本概念,包括实体、属性、码、域、实体型与实体集,并重点分析两个实体型、多个实体型及单个实体型内部的一对一、一对多、多对多联系,配合映射基数说明及丰富的ER图示例,帮助读者理解抽象概念与图形化建模方法。资源为单个PPT文件,压缩包大小622KB,便于直接打开学习或用于课堂展示;目前已有411人学习浏览,适合课前预习、课后巩固或作为教学设计参考。通过本课件可快速建立数据库概念模型框架,理清实体间联系与ER图绘制规范,为后续关系模型转换和数据库逻辑设计打下基础。
1. 实体-联系模型是数据库设计的“第一张草图”
绝大多数数据库设计翻车,不在 SQL 写得差,而在 ER 图阶段少问了一句“这是一对多还是多对多”。实体-联系模型就是用实体、属性、联系三个构件,把业务语义画成一张可评审的图;ER 图则是这张图的表达形式。在《数据库系统概论》(国内高校用的王珊、萨师煊版本已更新到第六版)里,E-R 模型是整个概念设计章节的地基,但它不只是考试课:它是团队对齐需求、转关系模式、排查冗余结构的起点。这里按实体-联系模型从概念到落地的路径走一遍,覆盖 ER 图怎么画、主键怎么标、联系怎么定,以及从 ER 图转换到关系表时的常见坑,最后落到工具和自查技巧。
2. 实体、属性、联系:把现实世界翻译成 ER 图的三个构件
E-R 模型的表达力来自三个图形符号:矩形表示实体集,椭圆表示属性,菱形表示联系。这套命名从 Chen 提出的 Entity-Relationship Model 沿用至今。绘图本身不难,真正决定设计质量的是“分界线怎么划”——什么时候一个名词该建实体,什么时候该挂成属性,什么时候该画一条联系。
2.1 实体与属性:分界线在“要不要单独维护”
业务里最常见的争论是“学校名称算学生的属性吗”“手机号算客户的属性吗”。通常我会先问一个问题:这个值如果发生变化,是否会同时影响多条记录?
把“学校名称”作为学生的属性,全校上万个学生记录都要冗余存储一次,学校改名时需要全表更新,漏掉一条就出现数据不一致。把“学校”拆成独立实体,学生表只存 school_id,学校名称只维护一份,这是典型的从“会不会被复用”判断实体边界的思路。判定的常用标准有三条:
- 该名词对应的数据是否会被多处引用(比如“银行网点”会被多个柜员、多台设备引用);
- 该名词是否有独立于当前实体的增、删、改生命周期(比如“客户”即使没有账户也还存在于系统中);
- 该名词是否天然是复数集合(“订单明细”绝不会只有一条,“账单”往往是多条的)。
如果以上任意一条成立,就应该建实体,而不是把它的全部信息压成某个实体的属性。反过来,一个只修饰当前实体、不会被其他地方引用的描述(如客户的出生日期),就老老实实做属性。
2.2 属性与主键:ER 图中主键的标准画法
“er图主键怎么表示”是高频搜索。标准的 Chen 风格画法是:在该属性名的下方画一条下划线,代表它是这个实体集的主键。比如“客户”实体下,“身份证号”加下划线,阅读时读作“客户的主键是身份证号”。没有主键的实体集,在真正的实体-联系模型里是不完整的。
多值属性用双椭圆表示。一个客户留了三个手机号码,这三个号码不是三个属性列,而是一个“联系电话”属性,取值为一个集合。转换到关系表时,多值属性不能直接塞成一个字段,否则就违反第一范式;通常拆出一张从表或中间表来容纳这些值。
派生属性用虚线椭圆表示。账户的“余额”可以由流水累计得出,属于派生属性。这类属性在 ER 图上标注出来,是为了提醒转换到关系模式时不要贸然建一个物理列去存储,否则每次存取款都要同步更新“余额”,既容易丢更新也容易造成数据不一致。
需要刻意区分的一点:外键不是 ER 图里的符号。外键是从 E-R 模型转换到关系模式时才出现的概念。如果画 ER 图时就在实体框里写“customer_id (FK)”,等于把逻辑模型混进了概念模型,评审时才看不出语义问题。
2.3 联系与基数:三种联系的判别方法
联系在 ER 图里是菱形,菱形里写动词(“选修”“下单”“开户”)。判断联系的类型不能靠感觉,要靠两个反向提问:从 A 的实例出发,最多对应几个 B 的实例;从 B 的实例出发,最多对应几个 A 的实例。
| 联系类型 | 判别特征 | 典型示例 | 转关系模式时的处理 |
|---|---|---|---|
| 1:1 | A 的一端最多 1,B 的一端最多 1 | 部门与部门经理 | 一端主键并入另一端表,或低频时合并为一张表 |
| 1:N | A 的一端最多 N,B 的一端最多 1 | 客户与账户 | N 端表中加入 1 端表的主键作为外键 |
| M:N | A 的一端最多 N,B 的一端最多 N | 学生与课程 | 必须新建中间表,两端主键联合做主键 |
判别时最有效的话术是:一个客户能开多个账户,所以从客户看账户是多;一个账户只能属于一个客户,所以从账户看客户是一,合起来是 1:N。反过来,一个学生选多门课、一门课被多个学生选,两个方向都是多,就是 M:N。
还有一个容易被忽略的实体变体:弱实体。弱实体自己没有完全的主键,必须借助所属实体的主键才能唯一标识。典型的例子是“订单明细”:明细的行号只在订单内部有意义,离开订单号,明细 2 号是谁的?ER 图中弱实体用双矩形,弱联系用双菱形,转换关系模式后把外部实体的主键并入自身主键,形成联合主键。
3. 从需求到 ER 图:以银行储蓄系统为例的建模流程
搜索里“银行储蓄系统er图”出现频率很高,下面用它把完整流程走一遍。建模不是坐下就画矩形,而是“需求采集 → 边界圈定 → 实体候选 → 属性补充 → 联系定基数”五步递进。
3.1 第 0 步与第 1 步:圈定系统边界,从名词里挑候选实体
先确定这个系统管什么、不管什么。银行储蓄系统的边界是“存取款及相关账户管理”,利率市场化调整、贷款审批不在边界内。然后打开需求文档,把名词全部圈出来:客户、账户、储蓄卡、存款记录、取款记录、流水、银行网点、柜员、利率表、地址、电话、余额。
这些名词不可能全部成为实体。按上一章的标准筛一遍:
- “利率表”被多个账户产品引用,且利率会调整,建独立实体;
- “余额”可由流水计算,属于派生属性,不建实体;
- “电话”“地址”是客户或网点的描述属性,先挂在对应实体下,后面看是否多值再决定是否拆表;
- “存款记录、取款记录”本质都是账户下的资金变动,统一抽象成“交易流水”一个实体,避免后面画图时出现多条平行的“存款联系”“取款联系”。
这一步产出的是一个候选实体清单:客户、账户、储蓄卡、交易流水、银行网点、柜员、利率表。
3.2 追加属性并圈定主键
给每个候选实体补属性。“客户”实体需要有身份证号、姓名、手机号、住址;“账户”实体需要有账号、开户日期、币种、账户状态;“交易流水”实体需要有流水号、交易类型、交易金额、交易时间、交易后余额。
主键判定这里有一个顺序:先找业务主键,没有业务主键再考虑自增代理主键。“客户”用身份证号做主键时要注意,实际系统里会出现一个身份证对应多个客户号(柜面渠道和线上渠道建了两个客户档案),所以银行业普遍用内部生成的客户号做主键。这样在 ER 图阶段就把“业务上唯一”和“技术上唯一”区分清楚了。
手机号在这个实体集里是一个多值属性,因为一个客户可能留了家庭电话、办公电话、手机三个号码。在 ER 图上,客户的“联系电话”画成双椭圆,右端连着客户矩形。这一笔处理,后面转关系模式时会直接决定要不要多建一张联系方式表。
3.3 用已有数据验证基数假设,而不是只信需求会议
需求评审会上产品经理说“一个客户可以开多个账户”,这是需求方的意图,不等于存量数据就是这样的。尤其在接手遗留系统时,写代码前我会先对真实数据做一次基数探测,用事实修正 ER 图的连线。
探测“账户-流水”是否为 1:N,执行下面的 SQL:
-- 验证: 一个账户是否对应多条流水 -- 若返回非空行, 说明至少存在一对多关系 SELECT account_id, COUNT(*) AS tx_count FROM transaction_tbl GROUP BY account_id HAVING tx_count > 1 LIMIT 5;逻辑说明:按 account_id 分组,统计每个账户下的流水条数,HAVING 过滤出条数大于 1 的账户。如果查询结果为空,说明当前数据里每个账户最多只有一条流水,需要进一步和业务方确认是否存在存量数据清理导致的信息丢失,不能直接据此认定 1:1。如果返回 5 行,每个 account_id 的 tx_count 都大于 1,则“账户到流水是一对多”的假设得到了数据支撑。
同样的探测可以反向做一次,验证“流水-账户”不会出现一条流水归属多个账户的情况:
-- 验证: 一条流水是否只属于一个账户 SELECT transaction_id, COUNT(DISTINCT account_id) AS acct_cnt FROM transaction_tbl GROUP BY transaction_id HAVING acct_cnt > 1;参数说明:COUNT(DISTINCT account_id) 统计单条流水关联的不同账户数,如果结果非空,说明流水与账户是多对多,ER 图上这条线就要重画,通常意味着“流水”里还隐藏着一个“被拆分的交易”实体。
3.4 处理多值属性与派生属性后再定稿
客户的多值属性“联系电话”拆成“客户联系方式”实体,与原“客户”实体形成 1:N 联系;外键为 client_id。账户的“余额”作为派生属性在 ER 图上保留虚线椭圆标注,但转关系表时不在账户表里建列。
这里有一个可复用的判断:如果业务要求快速展示“账户可用余额”,且流水量很大,每次实时汇总性能不够,那么可以专门建一张“账户余额快照表”,每天晚上批量重算。快照表在 ER 图上应作为一个独立实体出现,它的行数等于账户数,和流水表的数量级不同,不要把它当成派生属性挂掉。
3.5 合并与简化:消灭冗余联系
实体和属性都齐了以后,审视联系是否存在冗余路径。一个典型场景:客户到流水,是否要直接画一条“客户-流水”联系?
从语义上说,客户和流水确实间接相关,但中间已经存在“客户-账户”“账户-流水”两条联系。如果再加一条“客户-流水”,造成的后果是:评审人员容易误认为流水可以直接挂在客户下而产生扇出(fan trap),转关系模式时也可能诱导开发者写出跳过账户表的跨表查询。正确做法是删掉这条冗余联系,保持 ER 图的路径唯一性。
同样的流程换成教学管理系统也一样:学生、课程、教师、教务员四个实体,“选修”联系连接学生和课程,“授课”联系连接教师和课程,中间不会再画“学生-教师”的直线。这套“从名词出发→逐层筛选→用数据验证”的做法可以复用到任何系统的 ER 建模。
4. 从 ER 图到关系模式:转换规则与容易踩的坑
ER 图的价值最终体现在能不能正确变成一组表结构。转换过程有明确规则,也有大量“看着对、做起来错”的细节。
4.1 三条转换规则,背熟不如用熟
| 联系类型 | 转换策略 | 需要加的约束 |
|---|---|---|
| 1:1 | 将一端实体表的主键并入另一端实体表,作为外键 | FOREIGN KEY + UNIQUE |
| 1:N | 将 1 端实体表的主键并入 N 端实体表,作为外键 | FOREIGN KEY + 普通索引 |
| M:N | 新建中间表,两端实体表的主键都进入中间表 | 联合主键或 联合唯一索引 |
1:N 合并时,外键加在 N 端。比如“账户”是 N 端,那么账户表里要有 bank_id 指向网点表。为什么不能放在 1 端?因为把外键放在“网点”表里,一个网点维护一个账户,就限制了这个网点只能服务一个账户。
M:N 必须新建中间表。比如“客户”和“账户”是 M:N(一个客户可开多个账户,一个账户可有多个联合持有人),中间表里放 client_id 和 account_id。中间表的主键由两端主键联合构成,联合主键天然保证同一对关系只能插入一次。
1:1 是三种类型里自由度最大的。如果两边属性几乎总是一起查询、生命周期相同(比如“用户”和“用户档案”),我一般建议直接合并成一张表;只有在两个对象的数据敏感级别不同、访问频率差异极大时才拆开,比如把登录凭据和用户资料分表。拆开时哪一端并入哪一端,取决于查询入口:经常从哪边出发查对面,就把哪边的主键放进对方的表。
4.2 从银行储蓄系统 ER 图落成 DDL
把上一章设计的 ER 图按规则转成 SQL。核心语句如下:
-- 1:N: 账户表保留 bank_id 外键, 指向网点表 CREATE TABLE account ( account_id BIGINT PRIMARY KEY, client_id BIGINT NOT NULL, bank_id BIGINT NOT NULL, currency_code CHAR(3) NOT NULL, status TINYINT NOT NULL DEFAULT 1, open_date DATE NOT NULL, CONSTRAINT fk_account_client FOREIGN KEY (client_id) REFERENCES client(client_id), CONSTRAINT fk_account_bank FOREIGN KEY (bank_id) REFERENCES bank(bank_id) ); -- 联系属性: “开户”发生在某个时点, 放在账户表上更合适 -- 如果开户渠道、开户柜员只在开户时有效, 可以挂在账户表逻辑说明:账户表通过两个外键同时连接“客户”表和“网点”表,分别对应“客户-账户”的 1:N 和“网点-账户”的 1:N。client_id 和 bank_id 都使用了 NOT NULL,因为任何一个账户都必须能追溯到唯一客户和唯一网点。
再看 M:N 的中间表写法:
-- M:N: 客户与账户的联合持有关系 CREATE TABLE account_client ( account_id BIGINT NOT NULL, client_id BIGINT NOT NULL, holder_type CHAR(1) NOT NULL, -- 主持有人/副持有人, 属联系属性 create_time DATETIME NOT NULL, PRIMARY KEY (account_id, client_id), CONSTRAINT fk_ma_acct FOREIGN KEY (account_id) REFERENCES account(account_id), CONSTRAINT fk_ma_client FOREIGN KEY (client_id) REFERENCES client(client_id) );参数说明:联合主键 (account_id, client_id) 是这里的关键,它同时承担了“关系唯一”和“查询主路径”两个职责。holder_type 是联系自身的属性,比如区分主账户持有人和共同持有人,这类属性只能挂在中间表上,挂到客户表或账户表都会造成语义错位。
4.3 转关系时常见的三个“看着没问题其实错了”
第一个是把多值属性直接建成单列。客户有三个电话,开发者在客户表里建 phone1、phone2、phone3 三列,或者用逗号拼在一个 phone 字段里。前者扩展性差,后者违反第一范式且无法用索引查询。正确做法参照上一章的判断:如果号码数量有限且固定,可以拆成三个可空列;如果数量不固定,就建子表。
第二个是为派生属性建物理列。账户表存了一个 balance 列,业务在每次交易后同步更新它,结果出现并发时丢失更新,或对账时余额和流水对不上。派生属性要么用视图实时计算,要么用“余额快照表”定期重算,并把余额的准确性纳入对账流程。
第三个是 M:N 中间表用了自增主键却没加联合唯一约束。中间表 id 自增、PRIMARY KEY(id)、但没有 UNIQUE(account_id, client_id) 时,同一个客户和同一个账户的关联关系可以插入两次。查询统计时出现重复计数。标准做法是联合主键或者联合唯一索引,自增 id 仅作为辅助,不要让它成为唯一防线。
5. 用工具落地 ER 图:逆向工程与设计质量自查
手画 ER 图适合学习和评审,工程落地时建议用工具。MySQL Workbench 是覆盖面最广的选择,功能上支持从现有表结构逆向生成 ER 图:打开 Workbench,选择菜单 Database → Reverse Engineer,按向导连接目标实例,选中要导出的 schema,工具会读取全部表、字段、外键约束,自动生成一张含连线关系的 ER 图,之后通过 File → Export 导出为 SVG 或 PNG 直接贴进设计文档。对于一张表写好了 SQL 但还没建库的场景,可以找支持 SQL 转 ER 图在线工具,把 CREATE TABLE 和 ALTER TABLE 语句粘贴进去,工具解析外键后自动出图,适合快速检查别人设计的表结构。再往前一层,PowerDesigner 是经典教材里常提的重量级工具,流程是先在 CDM(概念数据模型)里画实体和联系,再一键转成 PDM 物理模型生成建表脚本,适合需要严格管理设计文档的团队。
逆向生成的 ER 图只能反映当前物理表结构,查不出“该建索引却没建”的问题。我常用一张自查 SQL 检查外键列上是否缺少索引:
-- 检查所有外键约束是否对应有索引 SELECT kcu.table_name, kcu.column_name, kcu.constraint_name, IF(stat.index_name IS NULL, 'NO INDEX', 'OK') AS idx_status FROM information_schema.KEY_COLUMN_USAGE kcu LEFT JOIN information_schema.STATISTICS stat ON stat.table_schema = kcu.constraint_schema AND stat.table_name = kcu.table_name AND stat.column_name = kcu.column_name WHERE kcu.referenced_table_name IS NOT NULL AND kcu.table_schema = 'your_database_name';逻辑说明:子查询把外键定义和 STATISTICS 做列名级匹配,检查每一条 FOREIGN KEY 对应的列上是否真实存在索引。返回 idx_status 为 NO INDEX 的行就是性能隐患。参数说明:your_database_name 替换为实际库名;外键如果有复合索引的一部分,STATISTICS 表里可能只匹配到其中一列,此时需要人工确认复合索引的列顺序是否能覆盖外键查询。
最后给一个成本最低的长期习惯:ER 图阶段就统一命名。主键统一命名为 id 或 实体名_id,外键统一命名为 关联实体名_id,联系表用两实体名加下划线连接(account_client),主键一律联合主键或带前缀的代理键。命名统一后,任何一张 ER 图转出来的 SQL 都能被别人秒懂,也不需要额外画一张“命名对照表”。
本文还有配套的精品资源,点击获取