图书管理系统数据库设计:E-R图、数据流图与关系模式实战

📅 发布时间:2026/10/3 7:55:10
图书管理系统数据库设计:E-R图、数据流图与关系模式实战
简介这份PDF资源面向数据库课程设计、期末考试复习及图书管理系统开发初学者以系统化方式整理了数据建模的核心内容。文档先用E-R图清晰标出管理员、读者、书籍、书架、阅览室五大实体及其属性关联再通过数据流图展现办证、借书、还书、缴费等关键业务流程随后给出完整关系模式与数据库字段定义涵盖各表主外键、数据类型、取值范围、默认值与空值约束等细节可直接用于建表语句编写、逻辑结构设计和系统功能实现。资源为单个PDF文件压缩包共约332KB内容紧凑便于快速查阅已有2700余人下载学习适用于需要完成课程报告、答辩讲解或系统设计的前中期开发者能有效节省从零梳理数据模型的时间。1. 从数据流到关系模式图书管理系统E-R图到底在画什么一份《图书管理系统E-R图、数据流、关系模式.pdf》通常是数据库课程设计里最难啃的部分。图纸画得漂亮照着建库却经常翻车借书记录对不上号、超期查询结果错乱、跑统计SQL时发现缺字段。根据我做过的几个小型图书系统方案问题大多不在画图技巧而是出在实体拆分上——尤其是“图书”这个实体十个设计里有八个没拆开。这篇笔记把数据流图、E-R图、关系模式三段工作串起来讲给出能直接照着敲的建表SQL和四条高频踩坑记录适合正在赶课设的学生也适合第一次做后台管理系统的后端开发。2. 数据流图先于E-R图把借书还书流程拆成四要素我一般接到这类系统不会先动E-R图而是先画数据流图DFD。原因很简单E-R图回答的是“数据长什么样、之间有什么关系”数据流图回答的是“数据从哪来、经过哪些处理、最后存到哪”。不先把流程理清楚E-R图上的联系全靠猜画出来的图跟业务对不上。2.1 顶层图先圈出外部实体与系统边界数据流图有四个基本要素外部实体、处理、数据存储、数据流。图书管理系统的顶层图里外部实体一般是读者和图书管理员系统内部是一个大的处理过程底下挂着若干数据存储。顶层图的价值是把系统边界画出来——哪些人跟系统打交道系统需要保存什么数据一眼就能看明白。用传统Yourdon记号描述顶层图大致长这样外部实体“读者”发出一条“借阅请求”数据流进入系统外部实体“图书管理员”输入“读者信息维护”“图书信息维护”等操作指令系统内部的处理完成后向“读者”返回“借阅凭证”向“图书管理员”输出“逾期提醒”“统计报表”底层的数据存储包括读者信息、馆藏信息、借阅记录。画顶层图最容易犯的错是边界外扩把“图书采购”“读者罚款缴纳”这些子系统的功能也塞进来。图书管理系统的边界一般就是借书、还书、续借、读者管理、馆藏管理这几件事别的都可以往后放。边界没画清楚后面关系模式会越做越臃肿。2.2 逐层分解借书流程的二级数据流顶层图只描述系统整体下一步把“借阅管理”这个处理再分层。借书流程拆成三个子处理验证读者身份、检查馆藏状态、登记借阅并更新状态。以借书为例二级数据流的走向是这样的处理1.1 验证读者输入读者号向“读者信息”数据存储发起查询返回读者资格信息是否停用、是否超期未还。处理1.2 检查馆藏输入馆藏号向“馆藏信息”数据存储查询返回该副本当前是否在馆、是否破损。处理1.3 登记借阅上述两步都通过后向“借阅记录”数据存储写入一条借阅数据同时向“馆藏信息”数据存储发起状态更新把在馆改为借出。这里要注意一个细节借书流程的数据流是双向的。处理1.1既要读“读者信息”也会在发现读者恶意借书不还时更新读者状态处理1.3既要写“借阅记录”也要写“馆藏信息”。很多人在数据流图上只画单箭头结果漏了状态更新这条数据流后面关系模式里馆藏表的“状态”字段自然就丢了。2.3 数据字典让每条数据流不再是黑匣子数据流图画完下一步是给每条数据流写数据字典。这一步最容易被跳过去但它是连接DFD和E-R图的桥梁——关系模式里的字段大部分能在数据字典里找到来源。拿“借阅登记”这条数据流举例数据字典条目可以这样写数据流名称借阅登记组成读者号 馆藏号 借书日期 应还日期 经手管理员号来源处理1.3 登记借阅去向数据存储D3 借阅记录再写一条“逾期提醒”数据流名称逾期提醒组成读者号 读者姓名 馆藏号 应还日期 逾期天数来源处理2.2 超期检查去向外部实体 读者写数据字典时如果发现某条数据流的组成字段说不全说明流程没想透。比如“逾期提醒”里要不要读者姓名要因为通知单直接打印给读者。那读者姓名从哪来从“读者信息”数据存储查。这个字段既然在数据字典里出现E-R图里就一定要给读者实体配上“姓名”属性。数据流图和数据字典做完实物交付物里最薄的一层就落地了。接下来可以动E-R图。3. 拆E-R图的实体与联系为什么“图书”必须拆成书目和馆藏E-R图是整个设计里最容易画得好看的图三个矩形两个菱形就能拼出一张。但也最容易画得“假”实体找不对联系标错度数转关系模式时才发现主键撞车、外键无处安放。这一章说清实体怎么找、联系怎么定、属性怎么归。3.1 实体识别从一个名词到一张表从数据流图里找名词是识别实体最稳的办法。图书管理系统里最常见的候选名词是读者、图书、管理员、出版社、借书、还书。其中“借书”“还书”是动作对应E-R图里的联系不是实体“出版社”从信息量角度看很多小型系统不需要单独建表并入图书属性即可。真正的关键在“图书”这个词。我先说结论在E-R图上图书必须拆成“书目”和“馆藏副本”两个实体。原因很直接——图书馆里同一本书通常有好几本复本《三体》有5本这5本在书架上是5个物理位置分别可以被借出或留在馆。如果E-R图只画一个“图书”实体用ISBN做主键一行只能代表一本书5本复本无法表达不用ISBN做主键又没有任何一个属性能唯一标识一本书的物理副本。拆成两个实体后问题自然解决。实体属性主键候选书目ISBN、书名、作者、出版社、分类号、价格ISBN馆藏副本馆藏号、ISBN、所在馆、上架日期、状态馆藏号读者读者号、姓名、性别、读者类型、单位、手机号、办证日期读者号管理员管理员号、姓名、登录密码、角色管理员号馆藏副本的“状态”字段不是可有可无它来自数据流图里处理1.3对馆藏信息的更新。状态取值至少要有“在馆”“借出”“破损”三种这是借书流程的判断依据。书目和馆藏之间的关系是1N一个书目对应多个副本这一点在联系设计时要写清楚。3.2 联系与度数读者与馆藏之间是M:N借阅实体定好后下一步找联系、标度数。图书管理系统里的核心联系有三个读者与馆藏之间的“借阅/归还”联系、书目与馆藏之间的“包含”联系、管理员与借阅之间的“经手”联系。读者与馆藏之间是M:N这是全图最重要的一个度数判断。一个读者可以借多本馆藏同一本馆藏在不同的时间点可以被不同读者借走所以两端都是多。这个联系还带着自己的属性借书日期、应还日期、归还日期、续借次数。带属性的M:N联系在转关系模式时不是简单地往某一端加外键而是单独拆成一张表这些属性全部落在这张表里。很多课程设计会把读者和图书之间的借阅画成1N理由是“同一本书同一时间只能被一个人借”。这是把“物理副本的当前状态”和“历史借阅记录”搞混了。借阅记录是历史流水同一副本被甲借完、归还再被乙借这是两条记录。从E-R图的角度在时间维度上就是多对多。管理员的经手联系是1N一个管理员可以处理多条借阅记录。书目和馆藏的包含联系也是1N。这两个联系度数简单转关系模式时按规则往N端加外键即可。3.3 属性归属只依赖主键的属性才进实体属性放哪个实体判断标准只有一条这个属性是否能由该实体的主键唯一确定。读者类型能否放借阅记录表不能读者类型只依赖读者号放进借阅表会出现大量重复值转关系模式时违反第二范式。书名能否放馆藏副本表不能书名依赖ISBN而馆藏号本身不决定书名塞进去会在副本表里产生冗余。有一个容易纠结的点是联系方式。一个读者可能有多个手机号从严格第三范式看应该拆出“读者联系方式”这个多值属性单独建表。但小型图书管理系统里这种需求很少真正出现我一般把它简化成读者表里的一个“手机号”字段省一张表数据一致性风险也低。E-R图阶段不必追求无限拆分把能明确的属性归属定对剩下的冗余问题在关系模式阶段用范式检查再收一遍。E-R图画完可以拿三个问题自查每个实体有没有唯一标识每个联系是否标了度数联系自身的属性是否只属于联系、没有塞进任一端实体三个问题都能答上这张图才值得往下转关系模式。4. 关系模式落地从E-R图到可用的建表SQLE-R图是概念层设计关系模式是逻辑层设计。这一章讲清楚怎么把E-R图转成一组规范的关系模式再落到能跑的建表SQL。转关系模式是有固定规则的按规则走不会丢东西。4.1 转换四条规则合并、拆分、主键迁移教科书上的转换规则用到图书管理系统里其实就四条规则一实体转关系模式实体的主键直接作为关系模式的主键。读者实体转成读者表主键读者号书目实体转成书目表主键ISBN。规则二1N联系把1端主键并入N端作为外键。书目1与馆藏副本N之间的包含联系把书目的ISBN并入馆藏表管理员1与借阅记录N之间的经手联系把管理员号并入借阅表。规则三M:N联系单独建关系模式两端主键进入新表联系自带的属性也全部进入新表。读者与馆藏之间的借阅联系单独拆成借阅记录表读者号和馆藏号都作为外键进来借书日期、应还日期、归还日期、续借次数也在这张表里。规则四1:1联系合并到任意一端。图书管理系统里几乎没有必须单独建模的1:1联系真碰到了往任意一端放外键即可不必为它单独建表。4.2 关系模式清单与第三范式检查按上面四条规则整个系统的关系模式清单如下关系模式主键外键说明读者读者号姓名性别读者类型单位手机号办证日期状态读者号无状态字段用于停用避免物理删除书目ISBN书名作者出版社分类号价格ISBN无出版社不单独建表并入属性馆藏副本馆藏号ISBN所在馆上架日期状态馆藏号ISBN一个书目对应多个副本借阅记录借阅号读者号馆藏号管理员号借书日期应还日期归还日期续借次数借阅号读者号、馆藏号、管理员号由M:N联系转换而来加了代理主键管理员管理员号姓名登录密码角色管理员号无角色区分权限这里有一个和教科书规则不一致的取舍需要说清楚教科书上M:N联系单独建表主键通常是两端主键的组合也就是读者号馆藏号。但借阅记录表里我额外加了一个“借阅号”做主键。原因很实际——同一读者可以在还书之后再次借同一本馆藏如果用读者号馆藏号做组合主键第二次借阅记录永远插不进去即使再加上借书日期做成三字段组合主键也会让所有关联借阅记录的外键变得很长。加一个自增借阅号当代理主键业务键读者号馆藏号借书日期上另建唯一索引既防重复又方便关联。第三范式检查过一遍借阅记录表里只存读者号不存读者姓名姓名由读者表通过外键关联查询没有传递依赖馆藏副本表里只存ISBN不存书名和作者书名由书目表提供消除了对ISBN的传递依赖读者表里每个字段都直接依赖读者号。这个结构满足3NF冗余主要控制在可接受的范围内。4.3 建表SQL最小可用版本与约束说明按上面的关系模式清单MySQL建表SQL可以一次性敲完。注意建表顺序外键依赖的表必须先建。-- 管理员表 CREATE TABLE admin ( admin_id CHAR(8) PRIMARY KEY, admin_name VARCHAR(20) NOT NULL, login_pwd VARCHAR(64) NOT NULL, role TINYINT NOT NULL DEFAULT 1 COMMENT 1管理员, 2超级管理员, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 读者表 CREATE TABLE reader ( reader_id CHAR(8) PRIMARY KEY, reader_name VARCHAR(20) NOT NULL, gender TINYINT NOT NULL DEFAULT 0 COMMENT 0未知, 1男, 2女, reader_type TINYINT NOT NULL DEFAULT 0 COMMENT 0学生, 1教师, 2校外读者, unit VARCHAR(50), phone VARCHAR(20), reg_date DATE NOT NULL, status TINYINT NOT NULL DEFAULT 1 COMMENT 1正常, 0停用 ); -- 书目表 CREATE TABLE book_info ( isbn VARCHAR(20) PRIMARY KEY, book_name VARCHAR(100) NOT NULL, author VARCHAR(50) NOT NULL, publisher VARCHAR(50), category VARCHAR(20), price DECIMAL(8,2), intro VARCHAR(200) ); -- 馆藏副本表 CREATE TABLE book_copy ( copy_id BIGINT PRIMARY KEY AUTO_INCREMENT, isbn VARCHAR(20) NOT NULL, location VARCHAR(20) COMMENT 所在库/架位, shelf_date DATE NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0在馆, 1借出, 2破损, CONSTRAINT fk_copy_isbn FOREIGN KEY (isbn) REFERENCES book_info(isbn) ); -- 借阅记录表 CREATE TABLE borrow_record ( borrow_id BIGINT PRIMARY KEY AUTO_INCREMENT, reader_id CHAR(8) NOT NULL, copy_id BIGINT NOT NULL, admin_id CHAR(8) NOT NULL, borrow_date DATE NOT NULL, due_date DATE NOT NULL, return_date DATE NULL COMMENT NULL 表示未归还, renew_count TINYINT NOT NULL DEFAULT 0, CONSTRAINT fk_br_reader FOREIGN KEY (reader_id) REFERENCES reader(reader_id), CONSTRAINT fk_br_copy FOREIGN KEY (copy_id) REFERENCES book_copy(copy_id), CONSTRAINT fk_br_admin FOREIGN KEY (admin_id) REFERENCES admin(admin_id) );字段类型的选择有几个参数可以按实际情况调。ISBN用VARCHAR(20)而不是CHAR(17)是因为ISBN-13带连字符一共17位但有些旧书是ISBN-10且有x结尾20位能吞下所有情况。读者号用CHAR(8)定长配合校园卡学号格式如果读者来源不统一改成VARCHAR(12)更稳。due_date是业务算出来的通常按借书日期加30天不建议在SQL里写默认值应该在INSERT语句里计算后传入。return_date允许为NULL这句话是整个超期查询的命根子NULL才代表“没还”不要用空字符串或“0000-00-00”代替。借阅记录表是查询最频繁的表group by和join基本都落在它身上。在reader_id、copy_id、return_date和due_date上建普通索引能明显提速。外键要不要加我的建议是这种小系统一定要加。外键是数据库自己保证引用完整性的机制课设和内部系统数据量不大那点写入开销换来的数据可靠比应用层代码检查划算得多。提示如果以后业务量涨到百万级借阅记录再考虑把外键去掉、把写入改成应用层事务控制。早期设计阶段保留外键是最省心的做法。5. 避坑与验证图书管理系统关系模式的五个高频翻车点关系模式这步出问题往往不是画图时错而是建完表跑数据时才暴露。下面几条是我见过也踩过的坑每条都按“现象 → 原因 → 解决”写。5.1 一本书一行主键撞车与多副本丢失现象借书模块上线后发现同一本《三体》只能被借出一次。第二个人来借时系统查不到任何在馆记录因为整个表里只有一行状态已经改成借出。原因建表时只建了“图书”一张表用ISBN做主键一行代表一本书。图书有五本复本但ISBN只有一条复本信息无处安放。这是把“书目”和“馆藏副本”两个概念混成了一个。解决必须拆成book_info和book_copy两张表。book_info存书目信息ISBN做唯一标识一本书只出现一次book_copy存物理副本每本复本一行用自增copy_id做主键ISBN做外键。借阅记录关联copy_id而不是isbn。E-R图阶段就把“图书”实体拆好这条坑就不会踩。5.2 return_date为0导致的超期误判现象写超期查询SQL时发现结果完全不对。没还的书全被过滤掉了反而显示出一堆刚借出去的书“已超期”。原因很多人在设计借阅表时给return_date设置了默认值或者用“0000-00-00”表示未归还。查询超期时脑子里想的逻辑是“curdate - return_date 30”但未还的记录return_date是0算出来的差值大到离谱自然误判。解决建表时把return_date设为NULL未归还就是NULL已归还是实际日期。超期查询写成SELECT r.reader_name, b.book_name, c.copy_id, br.borrow_date, br.due_date FROM borrow_record br JOIN reader r ON br.reader_id r.reader_id JOIN book_copy c ON br.copy_id c.copy_id JOIN book_info b ON c.isbn b.isbn WHERE br.return_date IS NULL AND br.due_date CURDATE() ORDER BY br.due_date;这个SQL先联合四张表把读者、书目、副本的信息拼在一起再用return_date IS NULL AND due_date CURDATE()筛出真正超期未还的。逻辑说明就一句话没还才算超期还了哪怕晚还也不在催还清单里。5.3 删除级联还是限制外键策略不能随手选现象管理员打算清理一个毕业生的读者记录结果数据库死活删不掉改成了级联删除之后历史借阅流水全没了学期统计报表直接变成一张空表。原因建外键时没考虑删除策略。直接删读者会被借阅记录的外键拦住改成ON DELETE CASCADE又会把借阅记录连坐删除。解决借阅记录是业务流水无论如何不能跟着读者一起删。正确做法是读者表保留status字段毕业读者做逻辑停用而不是物理删除。外键保持默认的RESTRICT阻止删除有业务关联的读者页面上的删除按钮代码里改成执行一条UPDATE reader SET status 0 WHERE reader_id ...。统计历史数据时读者信息还在只是状态变为停用。5.4 三个验证SQL方案可用性的试金石关系模式建完我习惯用三条业务SQL当试金石验证设计能不能支撑真实需求。第一条是查某个读者的全部借阅历史要求关联读者、借阅、馆藏、书目四张表第二条是查当前所有在借的书要求return_date IS NULL条件能用上索引第三条是统计出版社的图书总量验证分组查询不卡壳。-- 验证1按读者查全部借阅历史 SELECT r.reader_name, b.book_name, br.borrow_date, br.due_date, br.return_date FROM borrow_record br JOIN reader r ON br.reader_id r.reader_id JOIN book_copy c ON br.copy_id c.copy_id JOIN book_info b ON c.isbn b.isbn WHERE r.reader_id 20240001 ORDER BY br.borrow_date DESC;-- 验证2统计借阅量最高的5本书 SELECT b.book_name, COUNT(*) AS borrow_count FROM borrow_record br JOIN book_copy c ON br.copy_id c.copy_id JOIN book_info b ON c.isbn b.isbn GROUP BY b.isbn, b.book_name ORDER BY borrow_count DESC LIMIT 5;如果这三条SQL都能顺畅写出来、结果符合业务直觉那这个关系模式基本可用。任何一条写不出来都说明E-R图阶段漏了实体或者漏了联系回去改图比在建好的表上打补丁划算。6. 一个压箱底的习惯先写三条查询SQL再回头改E-R图6.1 用验收查询反向验证设计我在前文反复提到“用SQL验证设计”这背后有一个我自己养成的习惯每次设计E-R图和关系模式之前先把系统里最核心的三条查询SQL写出来。不是写完图再验证而是先拿SQL当验收标准再回头设计数据模型。拿图书管理系统举例开工前我会先列三条查询催还清单、某读者的借阅历史、热门图书统计。这三条查询几乎会遍历系统里所有实体催还清单要读者表、书目表、馆藏表、借阅表四表联查借阅历史要按读者号过滤热门图书要按书目分组聚合。如果E-R图设计完这三条SQL的字段都能找到来源、表都能关联上说明实体和联系没有漏。如果某条SQL写不出来往往是实体拆错或者联系度数标错。这个习惯帮我避免过好几次返工。有一次我就偷懒没先写SQLE-R图画得挺完整等建完表要查“某个架位上今天该还还没还的书”发现馆藏表里根本没存架位和应还日期的可关联路径只能回去在E-R图上给馆藏加属性、在借阅表上补索引。从那以后我每做一个管理系统都先把查询SQL敲进一个备注文件里画图时就对着它检查。等所有表建完把文件里的SQL原样跑一遍全部通过系统核心功能就有底了。这个习惯的成本几乎为零却能把数据库设计里最大的“黑匣子”提前打开。先想清楚要查什么再去设计存什么比画完图再补救踏实得多。希望帮到你。本文还有配套的精品资源点击获取