简介:数据库ER图习题.pdf是一份面向数据库初学者与备考学生的ER图概念模型练习资料。资源为单个PDF文件,大小仅83KB,轻量易存,适合零碎时间刷题。资料汇集了一批典型ER图设计题目,场景覆盖商业库存与销售、汽车运输、银行储蓄、体育锦标赛、高校教务、医院住院管理、证券营业和旅游管理等,每一题都给出了实体、属性及联系解析,重点说明如何将真实业务规则转化为实体间的一对多、多对多关系,并标注“库存”“供应”“聘用”“隶属”等联系的关键属性。已有2311人学习下载,适合正在学习数据库设计、考研复习或备战期末考试的读者使用。通过对照题目与解析反复练习,可有效提升从需求描述中识别实体、提炼属性、梳理联系的建模能力,为后续关系模式设计与SQL实现打好基础。
1. 数据库ER图习题PDF:先把这份资源能解决的问题说透
数据库设计这片领域里,ER图从来不是“画个矩形、拉条线”那么轻松。真正决定建模对错的,是你能不能从一段业务描述里认出哪些词该立成实体、哪些词只能当属性,以及两个实体之间的基数比到底是几比几。网上流传的这份数据库ER图习题PDF,不是教材也不是工具,而是一份把常见业务场景压成小小题目的训练集。它解决的问题非常具体:让你把零散的文字需求翻译成规范的ER图,再把ER图无损转换为关系模式,最后落成建表SQL和可执行的查询,顺带绕开考试和实际项目中反复出现的那些坑。适合正在备考数据库、要做课程设计,或者工作里想系统补一补数据建模基本功的从业者,也适合那些已经会写SQL、但一碰到“多对多关系怎么建表”就犯怵的人。
2. 实体、属性与联系:读懂习题里的三种核心建模元素
拿到一份ER图习题,第一步不是急着找矩形和菱形,而是先在题干里把“实体、属性、联系”这三类东西分开。很多题目的丢分点恰好就藏在这层分类里:同一个词,在A题里是实体,在B题里就只是属性。判断标准并不是词的形态,而是题干有没有围绕这个概念展开独立描述。
2.1 实体与属性:从题干文字里揪出矩形和椭圆
我习惯用一个很笨但有效的筛选法:把一个名词在题干里出现的次数和修饰它的形容词数出来。一个概念出现了三次以上,而且它前面挂着“名称、编号、状态”这类描述,那它就该画成矩形;如果它只是说明某个实体的特征,比如“学生的年龄”“图书的价格”,那它自然是椭圆里的属性。拿习题里很常见的“某高校学生信息管理系统”来说,“班级”这个词如果只是描述学生属于哪个班,那它就是学生实体的一个属性;但题目一旦再写“每个班级有班主任、教室、人数”,班级就该单独立成实体。这个判断直接决定后面表结构里要不要单拆一张班级表。
符号、语法和判断信号可以对照下面这张表,习题PDF里每一道题都能拿它套:
| 图形元素 | 图形符号 | 代表含义 | 判断信号 |
|---|---|---|---|
| 实体 | 矩形 | 独立存在的事物集合 | 有可唯一标识的码,有多个描述属性 |
| 属性 | 椭圆 | 实体的特征 | 挂在某个实体下,不单独承担业务逻辑 |
| 联系 | 菱形 | 实体之间的关联 | 题干中出现“每个”“一个”等对应关系字眼 |
| 派生属性 | 虚线椭圆 | 可由其他属性计算得到 | “年龄由出生日期计算”“总分由各科成绩加总” |
| 多值属性 | 双线椭圆 | 一个实体对应多个值 | “一个人有多个电话号码” |
主码的选择在习题里也是高频考点。学号、身份证这种天然唯一的字段适合做主码,姓名、性别这种重复率极高的字段直接排除。有些题故意写“学生有联系电话”,但没说一部还是多部,这是多值属性的信号,正确答案要么拆出“联系电话”表,要么用双线椭圆标出,绝不能把多个电话号码塞进同一个文本字段里。
2.2 联系类型:1:1、1:N、M:N 的判定信号与习题特征
联系是ER图建模里最值钱的部分。判断基数比的标准动作,是向自己提两个问题:一个X实体最多对应几个Y实体?反过来一个Y实体最多对应几个X实体?两个答案都固定是1,那是1:1;一边固定是1,另一边是多个,那是1:N;两边都是多个,那就是M:N。
习题题干里通常会藏信号词:“每门课程由一位教师讲授”——课程对教师是N:1;“每个仓库可以存放多种零件,每种零件可以存放在多个仓库”——这是经典M:N;“每个部门只有一个负责人,每个负责人只负责一个部门”——1:1。判断题做多了你会发现,M:N最常出现在“选课”“存放”“借阅”“参与”这几个动作性词汇后面,因为这些动作天然需要一张连接表去记录双方的多对多关系,同时还能顺手存下动作本身的附加属性,比如选课成绩、借阅日期、存放数量。
还有一种容易被忽略的题型是一元联系,也就是实体自己跟自己发生关联。习题里出现过“员工管理员工”一类的自环联系:一个员工管多个下属,一个员工只有一个直属上级。这种题在转换成关系模式时,处理方式是给员工表增加一个“上级编号”外键,指向员工表自身的主键。判定基数的方法和二元联系一模一样,只是两个参与方指向同一张实体表。
三元联系和“联系带属性”是拉分题。当一个联系本身有值得记录的信息时,比如“某个学生选某门课取得某个成绩”,成绩属于联系“选课”而不是学生或课程实体。转换关系模式时,M:N联系会被独立建成一张选课表,成绩列就放在这张表里。习题PDF里这类题目反复出现,练的其实就是这个思维习惯——先分清主谓宾,再画连线,最后决定属性挂在哪一端。
3. ER图转关系模式:从图形到表结构的四步映射法
ER图画得再漂亮,最终交付的还是一组关系模式。很多新人在这里踩的第一个大坑是“看图说话”:实体转表、属性转列谁都懂,但联系到底转成什么,教材讲得含糊,习题答案又直接跳过推导过程。我一般按下面这套步骤走,配合习题PDF练熟以后,基本不会漏字段。
3.1 普通实体与弱实体:一张表对应一个实体的边界条件
普通实体转关系模式的规则非常直白:实体名变成表名,属性名变成列名,实体主码变成表主键。真正需要多想一步的是弱实体。弱实体的特征是“离开了某个强实体就无法独立存在”,习题里常见的例子是“员工-家属”:家属没有全局唯一的编号,只有依赖员工工号和家属顺序号才能唯一标识。这种题转换成表时,家属表的主键必须是“员工工号 + 家属序号”的复合主键,同时“员工工号”也得作为外键指向员工表。漏掉父实体主键,表结构照样能建出来,但会造成第五张表里说的存在性依赖丢失问题。
3.2 三种联系的转换规则:外键放哪不再靠蒙
联系转关系模式,核心是“外键朝哪个方向放”。三条规则可以覆盖90%的习题:
| 联系类型 | 转换规则 | 外键位置 | 注意点 |
|---|---|---|---|
| 1:1 | 并入任意一端实体表 | 选择任一张表添加另一方的主键作为外键 | 建议并入查询频率高的一端 |
| 1:N | 并入N端实体表 | N端表添加1端表的主键作为外键 | 1端表不加外键 |
| M:N | 独立建一张中间表 | 中间表分别持有两端的实体主键 | 中间表可以再放联系属性 |
1:1联系的“并入任意一端”让很多新手纠结,其实选哪边在逻辑上都成立,真正判断依据是业务查询方向。比如“班级-班主任”这种1:1,如果系统里经常从老师查班级,就把班级编号放进教师表;反过来就放进班级表。1:N最典型的是“仓库-零件”改为“供应商-零件”场景,供应商表主键要出现在零件表里,而不是反过来。M:N里的中间表专有名词很多,连接表、联系表、交叉表说的都是它,习题PDF里你还会看到它被叫“选课表”“借阅表”,本质都是M:N关系的落地。
3.3 完整习题实战:一道综合题拆出完整关系模式清单
来走一遍习题PDF里我认为最有代表性的综合题,它同时包含了1:N和M:N两类联系,还带联系属性,覆盖面很广。
题干:每个仓库可以存放多种零件,每种零件可以存放在多个仓库,存放时记录数量;每种零件由唯一的供应商供应;仓库有仓库号、面积、电话;零件有零件号、名称、规格;供应商有供应商号、名称、地址。
第一步抽实体:仓库、零件、供应商,三个矩形。第二步抽属性并定主码:仓库号、零件号、供应商号分别做主码。第三步判断联系:“存放”是M:N联系,附加属性是存放数量;“供应”是1:N联系,一个供应商供应多种零件,但一种零件只有唯一供应商。第四步套规则,得到关系模式:
| 关系模式 | 字段 | 主键 | 外键 |
|---|---|---|---|
| 仓库 | 仓库号、面积、电话 | 仓库号 | 无 |
| 零件 | 零件号、名称、规格、供应商号 | 零件号 | 供应商号 → 供应商 |
| 供应商 | 供应商号、名称、地址 | 供应商号 | 无 |
| 存放 | 仓库号、零件号、存放数量 | 仓库号 + 零件号 | 仓库号 → 仓库;零件号 → 零件 |
注意零件表里那个“供应商号”:它来自1:N联系,被并入N端(零件是N);存放表里的“存放数量”则是M:N联系的属性,放不进仓库或零件任何一端,只能挂在连接表里。检查阶段的重点是核对每个习题需求是不是都被字段覆盖到了——“按仓库查零件”“按零件找供应商”“查某种零件在某个仓库里存了多少”,这三个需求分别落在存放表、零件表、存放表三处,没有遗漏。
4. 关系模式到建表SQL:把设计落成MySQL DDL的完整过程
关系模式清单确认后,下一步就是把它们变成能跑的建表语句。这里有两个容易让新手卡住的点:数据类型怎么选,外键约束要不要加。
4.1 选类型与写约束:仓库零件系统的建表SQL
接上一章的清单,我一般会写成下面这样:
CREATE TABLE supplier ( supplier_id CHAR(8) PRIMARY KEY, supplier_name VARCHAR(50) NOT NULL, address VARCHAR(100) ); CREATE TABLE warehouse ( warehouse_id CHAR(8) PRIMARY KEY, area DECIMAL(10, 2), phone VARCHAR(20) ); CREATE TABLE part ( part_id CHAR(8) PRIMARY KEY, part_name VARCHAR(50) NOT NULL, spec VARCHAR(30), supplier_id CHAR(8), CONSTRAINT fk_part_supplier FOREIGN KEY (supplier_id) REFERENCES supplier(supplier_id) ON DELETE SET NULL ); CREATE TABLE storage ( warehouse_id CHAR(8), part_id CHAR(8), quantity INT NOT NULL, PRIMARY KEY (warehouse_id, part_id), CONSTRAINT fk_storage_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouse(warehouse_id) ON DELETE CASCADE, CONSTRAINT fk_storage_part FOREIGN KEY (part_id) REFERENCES part(part_id) ON DELETE CASCADE );每个表都有它需要解释的地方。supplier_id、warehouse_id、part_id 用 CHAR(8) 而不是 INT 自增,是因为习题场景里的编号往往是业务编码,比如“SUP001”“WH0001”,定长字符串可以保证编码规则一致。part_name、supplier_name 用 VARCHAR(50),名称类字段可变长,50个字符通常够用;spec 用 VARCHAR(30) 足够容纳“M12×45”这类规格描述。area 用 DECIMAL(10,2) 而不是 FLOAT,面积数据要精确到两位小数,浮点数在统计汇总时会出玄学级的计算误差,DECIMAL不会。quantity 用 INT,数量不存在小数。
外键的 ON DELETE 策略我专门分开解释:part 表引用 supplier 时用 ON DELETE SET NULL,意思是供应商删除后零件还在,供应商号置空,保护业务数据;storage 表两个外键都用 ON DELETE CASCADE,因为存放记录本身就是依附于仓库和零件存在的,孤儿记录没有任何意义。
4.2 反向校验:把MySQL表结构导回ER图核对答案
写好SQL之后,我用一个习惯动作来验证建模是否丢东西:建完库表后,用建模工具做一次“由表还原ER图”。PowerDesigner 这一类的建模工具都支持反向工程,连上数据库,工具会自动把表之间的外键关系画成连线,生成一张可以直接和习题答案对照的ER图。这个动作对带外键的设计最有效,因为工具能直观显示出所有关联关系,哪种联系被漏掉、哪条外键方向放反了,一眼就能看出来。
如果手上没有图形化工具,也可以用 MySQL 自带的 information_schema 来核对外键关系:
SELECT TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = 'your_database' AND REFERENCED_TABLE_NAME IS NOT NULL ORDER BY TABLE_NAME;这段SQL把当前库里的所有外键约束列出来,看每一行的 TABLE_NAME 和 REFERENCED_TABLE_NAME 是否和关系模式清单一一对应。值得注意的是,连接表 storage 会用两个外键分别指向 warehouse 和 part,如果你看到了这两个外键记录,说明M:N联系落对了。
4.3 连接表两种主键策略:复合主键与代理主键的取舍
M:N连接表的主键设计是个经典二选一。习题里普遍采用复合主键,比如 storage 表的 PRIMARY KEY (warehouse_id, part_id),它天然保证了“同一零件在同一仓库只能有一条存放记录”,业务上完全吻合。但实际生产项目里,尤其在使用ORM框架的项目中,连接表常被加一个无意义的自增ID,这是出于框架便利性考虑,因为很多框架处理复合主键比较别扭。
两种策略没有绝对的对错,取舍表可以参考:
| 策略 | 优势 | 代价 | 推荐场景 |
|---|---|---|---|
| 复合主键 | 防止重复关联,查询直接命中 | ORM兼容性差,字段组合较长 | 习题作答、数据需要唯一性约束的库 |
| 代理主键(自增ID) | ORM友好,外键引用更短 | 需要另加唯一约束防止重复 | 生产系统、配合ORM框架开发 |
如果选择了代理主键,那么连接表的业务唯一性约束必须补上,否则同一对仓库-零件会被插入两条内容。正确写法是在连接表上再加一条 UNIQUE KEY,而不是只建自增主键不管其他字段:
CREATE TABLE storage_use_auto_id ( id INT AUTO_INCREMENT PRIMARY KEY, warehouse_id CHAR(8) NOT NULL, part_id CHAR(8) NOT NULL, quantity INT NOT NULL, UNIQUE KEY uk_wh_part (warehouse_id, part_id), FOREIGN KEY (warehouse_id) REFERENCES warehouse(warehouse_id), FOREIGN KEY (part_id) REFERENCES part(part_id) );这条 UNIQUE KEY 承担了原来复合主键的职责,UNIQUE 后面括号里那两个字段的先后顺序也有讲究,把它定义为 (warehouse_id, part_id),就是按“仓库→零件”的粒度约束唯一性。
5. 避坑与自查:翻车最多的五个ER图建模错误
习题PDF里除了题目,更值钱的是你自己做错之后留下的对照记录。我拿身边出现过的大量真实“翻车现场”做了五条归纳,每条都是现象、原因、解决三步讲清。
5.1 联系外键放错表:M:N被硬压成1:N
现象:学生选课关系里,把学生号外键放进了课程表,结果课程表里只能存一个学生,选修同一门课的第二个人一插入就报错。 原因:外键列天然只能表达“一对多”关系,M:N关系的两端都需要“多”的概念,靠单一外键字段装不下。 解决:承认这是M:N,必须新建独立的选课表,两端各放一个外键,选课表用 (student_id, course_id) 做复合主键。这是一个结构性问题,改字段长度、调约束都救不了。
5.2 派生属性照单全收:年龄存进表后逐年失真
现象:习题里写着“查询年龄大于20岁的学生”,有人就在 student 表里存了 age 列。过一年再跑统计,之前20岁的学生全部变成了21岁,数据还在,语义已经错了。 原因:年龄是由出生日期计算出来的派生属性,存进表里的只是某一个时间点上的快照,时间流逝后快照不会自动更新。 解决:表里存出生日期,查询时实时计算。SQL标准写法是WHERE TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) > 20。这符合习题里“不存储可由其他字段推导出的值”的规范,也避免以后每年要做一次全表更新。
5.3 连接表自增ID好用,但业务唯一性约束被忘
现象:选课连接表加自增ID后,同样的 (student_id, course_id) 被插入了两遍,后续统计课程人数时count翻倍甚至翻三倍。 原因:自增ID是代理主键,只保证自身唯一,它无法感知业务上不该重复的组合。复合主键消失时,原本由它承担的唯一性约束也没了。 解决:连接表有代理主键也能用复合唯一索引。在表上补UNIQUE KEY uk_stu_course (student_id, course_id),让数据库从约束层面拦截重复插入,而不是靠业务代码先查一遍再插。
5.4 弱实体漏挂父实体主键:存在性依赖全丢
现象:员工家属表只设计了家属自身字段,没有把员工工号纳入主键,结果两个员工的家属可能恰好顺序编号相同,数据串到别人头上。 原因:弱实体没有全局唯一的标识,“家属序号”只在同一个员工下才有效,脱离了父实体主键,它没有任何区分度。 解决:弱实体的主键必须由“父实体主键 + 判别属性”共同构成。比如家属表主键写成 (emp_id, dependent_id),同时 emp_id 作外键指向员工表。习题中凡是出现“依赖某实体才能存在”的表述,都要按这个规则处理。
5.5 三元联系基数误判:外键数量对不上号
现象:做“供应商-零件-项目”三元联系题时,按两两之间的二元关系转换,最后建出来的表要么少外键,要么多出好几张多余的连接表。 原因:三元联系是三个实体共同决定一条关联,不能单纯拆成两两关系来看。每一个组合都需要三方的参与条件一起去约束。 解决:先整体建一张三元连接关系表,里面放三个外键,再把三元联系中存在的局部二元1:N并入对应实体。例如“每个供应商供应多种零件”这个1:N,就让零件表带供应商号;“供应商和项目的供应关系”仍留在三元表里。先降维、再合并,顺序不能反。
6. 实例化数据反查法:用测试记录验证ER图是否真正成立
画完ER图并转成关系模式后,我最后永远会做一道工序——实例化反查。方法很简单:往每张表里插入两行足够典型的测试数据,然后拿题干中的查询需求去写SELECT,如果SQL写不出来或者结果明显违背业务逻辑,说明ER图在某个环节丢了信息。
拿仓库零件系统举例。题干说“查询每个仓库存放的零件总数量”,如果 storage 表设计对了,这条SQL能一口气跑通:
INSERT INTO warehouse (warehouse_id, area, phone) VALUES ('WH001', 1200.00, '010-12345678'); INSERT INTO part (part_id, part_name, spec, supplier_id) VALUES ('P001', '水阀', 'DN50', 'SUP001'); INSERT INTO storage (warehouse_id, part_id, quantity) VALUES ('WH001', 'P001', 200); SELECT w.warehouse_id, SUM(s.quantity) AS total_quantity FROM warehouse w LEFT JOIN storage s ON w.warehouse_id = s.warehouse_id GROUP BY w.warehouse_id;两行记录加一条聚合查询,就把“仓库和零件是否是M:N”“连接表是否漏了联系属性”“两个主外键是否对齐”三个疑点全部验了一遍。如果这条SELECT跑出来一条空记录,你就知道坚决不能开始写业务代码——模型错了,后面写什么都白搭。
还有一个进阶技巧:同一个需求,画出两种不同的ER图方案,用反查法判断哪种更好。比如“选课成绩”既可以是联系属性,也可以把“选课记录”拖出来立成一个实体。两边都能建表,但反查“重修补考成绩”时,联系属性方案要改表结构,实体方案只需要更新一条记录。这种取舍在习题PDF里不会直接写答案,但用反查法一测,哪种设计扩展性更强就一目了然了。
至于那份让自己反查失败的习题,我在纸面上把连接表、外键、复合主键全部重画了一遍,从那以后我建立了习惯,每画完一张ER图都强制走一遍“造记录→写查询→查漏项”的反查流程,模型的正确性基本就稳了。希望你也能用这个方法,少走几段我走过的弯路。
本文还有配套的精品资源,点击获取