简介这是一份面向软件工程或数据库课程设计的图书管理系统完整设计文档涵盖系统背景、需求分析、关系模式、E-R图、数据字典与数据表结构等核心内容适合需要完成数据库课设、理解图书馆业务建模的在校学生参考。文档以doc格式呈现含1个文件压缩包整体122KB虽体积小巧但内容完整便于直接阅读与修改使用。已有1897人学习下载。资源中详细给出了读者、书籍、类别、借阅、还书、罚款等模块的功能要求与实体关系设计并包含六类E-R图及书籍类别表、读者信息表、书籍信息表、借阅记录表、归还记录表和罚款记录表的字段定义可帮助读者快速梳理数据库设计思路、撰写课程设计报告或在此基础上扩展实现代码。1. 数据库课程设计图书管理系统这份资源能让你少熬两个通宵每年这个时候总有人抱着“数据库课程设计”这几个字发愁题目给了、报告模板有了一打开却是空白。这份图书管理系统课程设计就是典型的“软工期末答辩友好型”资源——它把需求分析、E-R 图、数据字典、关系模式、建表 SQL、初始化数据一条链全串起来了你拿到手就能照着建库、跑数据、写报告。我的判断是它适合两类人一类是还没动笔、想找个完整骨架的数据库课程设计新手另一类是已经建完表、被主外键和借还书流程卡住的熟手这里面的坑和解法比正文更有价值。别指望它是成品系统它是一套能直接复现的 SQL 脚本加文档底稿给你省下的是从零建模到踩完外键约束那几晚上的时间。2. 需求到表结构六张表怎么把十二项功能吃下2.1 十二项功能先收敛成实体再落到表原文的需求部分写了 12 条功能看着多其实能归成三条线读者管理输入、查询、修改、书籍管理类别标准、库存信息、借还管理借书、还书、超期罚款。数据库课程设计的答辩老师最喜欢问的一句话就是“你的需求怎么映射成表”所以第一件事就是把功能清单翻译成实体。这份资源的做法是标准的三层递进功能 → 实体 → 关系模式。先看关系模式原文给了六个这是整个库的地基书籍类别种类编号种类名称读者借书证编号读者姓名读者性别读者种类登记时期书籍书籍编号书籍名称书籍类别书籍作者出版社名称出版日期登记日期借阅借书证编号书籍编号读者借书时间还书借书证编号书籍编号读者还书时间罚款借书证编号读者姓名书籍编号书籍名称读者借书时间注意一个细节原文的罚款关系模式里把“借书证编号”写了两次“读者借书时间”也不全这是报告里的笔误建表时以数据字典里的 reader_fee 表为准读者借书证编号、读者姓名、书籍编号、书籍名称、罚款金额、借阅时间。类似这种前后不一致我后面会专门用一章讲因为它就是答辩现场被追问的重灾区。实体到表的映射逻辑其实不复杂一个实体一张表实体属性就是列借阅、还书、罚款这类“动作”是联系集它们的表至少包含两端实体的主键加上自己的属性。比如借阅记录必须有读者和书两个主键再加一个借书日期。这一步想清楚后面写 SQL 就是填空。2.2 数据字典逐表拆解主键、外键、非空约束这份资源最值钱的部分之一是数据字典六张表的字段、类型、是否为空、主外键都列全了。我把它整理成一张对照表你照着这张表建库基本不会错表名主键外键关键非空字段业务含义book_stylebookstyleno无bookstyle书籍类别被书籍表引用system_readersreaderid无readername, readersex读者含学生和教师两类system_booksbookidbookstyleno 引用 book_stylebookname, bookstyleno, isborrowed书籍库存含借出标记borrow_record原文为 bookidreaderid 引用 system_readersbookid 引用 system_booksborrowdate借阅流水return_record原文为 bookidreaderid 引用 system_readersbookid 引用 system_booksreturndate还书流水reader_fee原文为 bookidreaderid 引用 system_readersbookid 引用 system_booksbookname, bookfee超期罚款账单先别急着照抄这张表我标了“原文为 bookid”的地方就是后面要重点排雷的位置。简单说borrow_record、return_record、reader_fee 这三张表把 bookid 当主键意味着同一本书一辈子只能有一条借阅记录、一条还书记录、一条罚款记录——这显然不符合真实图书馆。但作为课程设计老师如果没深究这套结构也能交差如果你想稳稳过答辩第 5 章会给改法。字段类型上这份资源用的是 SQL Server 风格字符型统一 varchar时间用 datetime编号字段全部设为主键varchar 长度从 9 到 30 不等。注意 system_readers 的 readerid 只有 varchar(9)像借书证号 X05620207 正好 9 位如果学校编号格式更长建表前就得改长度这是后话。2.3 借还罚三个业务闭环怎么靠表关联表建好了业务闭环才能体现设计的合理性。这套系统有三个闭环对应三类 SQL 操作第一借书闭环。借书时往 borrow_record 插入一条记录读者、书、借书时间同时把 system_books 里的 isborrowed 从 1 改成 0。这里的 1 表示“在馆可借”0 表示“已借出”。原文的注释写的是“将已借出的借阅标记置 0”表述有点绕实际含义就是书被借走了要标记为不可借。第二还书闭环。还书时往 return_record 插入一条记录同时把 isborrowed 从 0 改回 1。原文只给了借书的 update还书方向的更新没给这也是个坑。第三罚款闭环。超期了往 reader_fee 里插一条罚款记录。罚款金额字段 bookfee 原文用的 varchar我建议你建表时改成 decimal(10,2)否则以后做金额计算、求和、排序都会出问题。这个细节答辩时主动说出来老师会觉得你考虑到了数据类型设计。这三个闭环全都要靠外键把读者、书籍、借阅串起来。外键的价值是数据库自己保证“借书的人必须存在、借的书必须存在”而不是靠应用层判断。所以建表顺序就变得很关键下一章直接进入可复现的脚本环节。3. 从 E-R 图到建表 SQL直接可跑的脚本与主外键设计3.1 实体属性怎么映射成列以 E-R 图为准还是以脚本为准这份资源里的 E-R 图是文字描述版没有真正的图文件画图时按实体展开即可书籍类别实体、读者信息实体、书籍信息实体、借阅记录信息实体、归还记录信息实体、罚款信息实体外加一个总的信息实体 E-R 图。E-R 图转关系模式有一条铁律1:n 关系把“一”端的主键放到“多”端当外键。这里的对应关系是书籍类别1→ 书籍nbookstyleno 作为外键进 system_books读者1→ 借阅记录nreaderid 作为外键进 borrow_record书籍1→ 借阅记录nbookid 作为外键进 borrow_record这里有一个前后矛盾得提醒你原文表 2-3 的书籍信息表里类别字段名叫 bookstyle但建表脚本里用的是 bookstyleno并且带了外键约束 references book_style(bookstyleno)。我一般以建表脚本为准因为外键约束明确指向了 book_style 表的主键 bookstyleno数据字典里那个 bookstyle 是复制报告时没同步的残留。这种不一致不会影响跑通但答辩老师翻到数据字典再对脚本一抓一个准。3.2 完整建表脚本照着跑SQL Server 2008 起都能用下面这份脚本我按原文的逻辑重排过保留了原表名和字段补上了被省略的约束修正了明显缺空格、缺逗号的位置并加了注释区分“原文如此”和“建议改动”。你在 SQL Server 里新建查询按顺序执行就行-- 1. 书籍类别表先建因为后续书籍表要引用它 create table book_style ( bookstyleno varchar(30) primary key, -- 类别编号 bookstyle varchar(30) -- 类别名称 ); -- 2. 读者信息表借书证编号为主键 create table system_readers ( readerid varchar(9) primary key, -- 借书证编号 readername varchar(9) not null, -- 读者姓名 readersex varchar(2) not null, -- 读者性别 readertype varchar(10), -- 读者种类学生/教师 regdate datetime -- 登记日期 ); -- 3. 书籍信息表外键引用 book_style create table system_books ( bookid varchar(20) primary key, -- 书籍编号 bookname varchar(30) not null, -- 书籍名称 bookstyleno varchar(30) not null, -- 书籍类别外键 bookauthor varchar(30), -- 书籍作者 bookpub varchar(30), -- 出版社名称 bookpubdate datetime, -- 出版日期 bookindate datetime, -- 登记日期入馆时间 isborrowed varchar(2), -- 是否被借出1在馆 0借出 foreign key (bookstyleno) references book_style(bookstyleno) ); -- 4. 借书记录表原文把 bookid 当主键语义有问题见第 5 章 create table borrow_record ( bookid varchar(20) primary key, -- 书籍编号原文作为主键 readerid varchar(9), -- 读者借书证编号外键 borrowdate datetime, -- 借书时间 foreign key (bookid) references system_books(bookid), foreign key (readerid) references system_readers(readerid) ); -- 5. 还书记录表 create table return_record ( bookid varchar(20) primary key, -- 书籍编号 readerid varchar(9), -- 读者借书证编号 returndate datetime, -- 还书时间 foreign key (bookid) references system_books(bookid), foreign key (readerid) references system_readers(readerid) ); -- 6. 罚款单表 create table reader_fee ( readerid varchar(9) not null, -- 读者借书证编号 readername varchar(9) not null, -- 读者姓名 bookid varchar(20) primary key, -- 书籍编号原文作为主键 bookname varchar(30) not null, -- 书籍名称 bookfee varchar(30), -- 罚款金额建议用 decimal(10,2) borrowdate datetime, -- 借阅时间 foreign key (bookid) references system_books(bookid), foreign key (readerid) references system_readers(readerid) );这段脚本的逻辑说明建表顺序是严格的“先父后子”book_style 和 system_readers 不依赖任何表必须最先建system_books 引用了 book_style 的主键所以排第三borrow_record、return_record、reader_fee 同时引用书籍和读者两张表必须最后建。如果你把顺序打乱SQL Server 会直接报“引用了无效的表”之类的外键错误。参数层面有几个值得注意的地方。isborrowed 用的是 varchar(2)存‘0’或‘1’原报告这么写是图省事实践中我建议改成 bit 或 int 类型避免后面写 where isborrowed 1 时出现类型隐式转换性能虽然在这点数据量上无感但答辩时能说出“isborrowed 是状态标记应该用布尔语义的类型”这句话印象分会不一样。bookfee 原脚本是 varchar(30)这在金额字段上是明显的类型误用varchar 存金额会导致排序按字典序、求和直接报错或转类型失败。3.3 换到 MySQL / 达梦 / 人大金仓要注意什么如果你用的是 MySQL 而不是 SQL Server这份脚本需要做三处调整。第一引擎要指定 InnoDBMyISAM 不支持外键约束第二datetime 类型两边都能用但 MySQL 的严格模式对日期字符串更敏感建议统一写成2005-09-23 14:23:56这种标准格式第三外键约束语法基本一致但 MySQL 要求外键列必须有索引好在这几个外键列本身要么是主键要么是参与唯一约束问题不大。如果是达梦或人大金仓这类国产数据库兼容模式选“SQL Server 兼容”或“MySQL 兼容”都行建表语句基本能直接跑个别差异在字符串长度单位上——达梦的 varchar 长度按字节算中文占 2 字节varchar(30) 存 30 个中文会超出。稳妥的做法是但凡要存中文的字段长度至少翻倍。这也是国产数据库课程设计里最常见的翻车点先提前说一声。4. 初始化数据与借书状态机isborrowed 的联动更新4.1 先灌类别和书籍注意原文的笔误位置表建好之后就是初始化数据。这里的顺序也有讲究先插 book_style类别再插 system_books书籍因为书籍表有外键指向类别。原文的初始化脚本有几处笔误最明显的一处是insert intotem_books(...)少了 system_ 前缀直接跑会报“对象名 tem_books 无效”。我把修正后的类别和书籍初始化写在一起-- 书籍类别初始化7 大类 insert into book_style(bookstyleno, bookstyle) values (1, 人文艺术类), (2, 自然科学类), (3, 社会科学类), (4, 图片艺术类), (5, 政治经济类), (6, 工程技术类), (7, 语言技能类); -- 书籍初始化节选 6 条作为样例完整 12 条按同样格式追加 insert into system_books(bookid, bookname, bookstyleno, bookauthor, bookpub, bookpubdate, bookindate, isborrowed) values (00125415152, 计算机组成原理, 6, 王爱英, 清华大学出版社, 2001-01-03, 2003-11-15, 1), (00456456, 数据库原理, 6, 萨师煊, 高等教育出版社, 2007-07-02, 2007-09-15, 1), (12215121, C 程序设计, 6, 谭浩强, 清华大学出版社, 2002-04-02, 2004-03-14, 1), (5455515, 中华历史 5000 年, 1, 吴强, 北京大学出版社, 2005-04-03, 2006-05-15, 1), (015115, 古代埃及, 3, 文华, 北京大学出版社, 2001-02-02, 2002-09-15, 1), (565800020, 探索宇宙奥秘, 2, 苏庆东, 北京大学出版社, 1999-02-28, 2000-01-21, 1);为什么类别和书籍的插入必须分开因为 system_books 的 bookstyleno 外键约束引用 book_style.bookstyleno外键保证了一件关键事情类别不存在书就插不进去。反过来如果先插书SQL Server 会报外键冲突提示你 bookstyleno 的值在父表里找不到。课程设计里这条报错本身就可以写进“调试记录”一节说明你理解了外键的参照完整性。isborrowed 字段在初始化时全部设为 1含义是“在馆可借”。这个字段是整个系统状态机的核心一本书的可用状态只由它决定而不是靠判断有没有借阅记录。这样做的原因是查询快——查where isborrowed 1就走普通索引条件不用做子查询去 borrow_record 里翻。数据量大了以后这两种写法的性能差距会非常明显答辩时可以主动提这一点。4.2 读者数据初始化时间格式统一写标准格式读者表初始化注意两点一是 regdate 这类 datetime 字段原文写的是2005-9-23 14:23:56这种不补零的格式SQL Server 能解析但为了跨数据库兼容我建议全部补零。二是读者性别、种类这些短字段varchar(2) 和 varchar(10) 够用不要乱加长度。下面这段是修正后的读者初始化insert into system_readers(readerid, readername, readersex, readertype, regdate) values (X05620207, 远鹏, 男, 学生, 2005-09-23 14:23:56), (X05620206, 特, 男, 学生, 2005-09-30 13:24:54), (X05620204, 铭静, 女, 学生, 2005-09-27 11:24:54), (X05620202, 潘虹, 女, 学生, 2005-09-30 13:24:54), (008415, 蒋伟, 男, 教师, 2004-04-30 09:24:54), (001456, 叶风, 女, 教师, 2004-04-30 09:24:54);读者类型字段 readertype 这里只用了“学生”和“教师”两种对应原文关系模式里的“读者种类”。这个字段看似简单但它会影响后面的罚款规则——比如学生借期 30 天、教师借期 60 天。原报告里的罚款功能只算了金额没有算“怎么判断超期”到第 6 章我会补一个按读者类型区分借期的 SQL。4.3 借书流程必须是一个事务先插记录再改状态原文的借书初始化是“先 insert borrow_record再 update system_books set isborrowed0”这个思路是对的但没有写进事务。问题在哪如果 insert 成功、update 失败比如书已经被借走数据库里就会出现“有借阅记录但书还显示可借”的脏数据。课程设计的数据量小手工执行不容易暴露但老师如果让你同时开两个窗口模拟并发借同一本书这个设计立刻露馅。我一般会把它包成显式事务并且把更新条件加上isborrowed 1防止重复借出。下面这段直接可跑begin tran; -- 开启事务 -- 1. 插入借阅记录 insert into borrow_record(bookid, readerid, borrowdate) values(00125415152, X05620202, 2007-09-27 11:24:54); -- 2. 将书状态置为已借出(0)仅当当前状态是在馆(1)时才允许 update system_books set isborrowed 0 where bookid 00125415152 and isborrowed 1; -- 3. 检查受影响行数如果为 0 说明书已被借走回滚 if rowcount 0 begin rollback; -- 撤销刚才的 insert print 借书失败书不在馆或已被借出; end else commit; -- 全部成功提交事务这段脚本的逻辑说明begin tran之后的所有操作要么一起成功要么一起回滚。第 3 步用rowcount判断 update 是否真的改到了行——如果书已经被借走where 条件isborrowed 1匹配不到任何行rowcount 为 0说明这次借书的 insert 是无效的必须回滚否则会把一条不该存在的借阅记录留在表里。这里有一个原报告容易让人误解的地方它说“同时将在已借出的借阅标记置 0”字面看像是“把已借出的改成 0”但结合上下文真实语义是“这本书已经被借出所以把 isborrowed 改成 0”。一个是描述状态一个是执行动作你要是照着字面写反了就会出现所有书都是可借状态、借阅记录却一堆的怪象。这个状态机的正确定义是1 代表在馆、可借0 代表已借出、不可借还书操作要把 0 改回 1。5. 避坑排查主键语义、外键约束与时间类型的翻车点5.1 借阅表主键设错同一本书第二次借出直接报错现象照着原文建好表、灌好数据后想再借一次《计算机组成原理》书号 00125415152执行 insert 进 borrow_recordSQL Server 直接报“违反 PRIMARY KEY 约束不能在对象中插入重复键”。原因borrow_record 表把 bookid 设成了主键。数据库规则是主键不允许重复这等于在说“一本书只能有一条借阅记录”。但真实业务是同一本书可以被不同读者在不同时间反复借一条书号在借阅历史里应该出现多次。这个设计把实体和流水搞混了——bookid 是“书”的标识而借阅记录是“一次借阅行为”的标识。解决把主键从单列的 bookid 改成联合主键 (readerid, bookid, borrowdate)语义是“同一个读者在同一个时间借同一本书”才唯一。改造脚本如下第 3 章建表时直接用它替换原文定义-- 正确的借阅表主键设计联合主键 create table borrow_record ( bookid varchar(20) not null, readerid varchar(9) not null, borrowdate datetime not null, primary key (readerid, bookid, borrowdate), -- 一次借阅行为 foreign key (bookid) references system_books(bookid), foreign key (readerid) references system_readers(readerid) ); -- 如果已经按原脚本建好表用 drop 重建 drop table borrow_record;这个坑的深层教训是主键选的是“业务行为的唯一性”不是“某个对象的编号”。对象编号当主键只适合它本身确实全局唯一的场景比如书籍表的主键 bookid、读者表的主键 readerid到了流水表主键必须是“这次行为”的唯一组合或自增流水号。原报告里 return_record 和 reader_fee 也有同样问题return_record 应该用 (readerid, bookid, returndate)reader_fee 建议加一列 feeid int identity(1,1) 做主键。5.2 外键列名前后不一致数据字典和建表脚本对不上现象按表 2-3 数据字典的字段名去写 insert发现 system_books 表根本不存在 bookstyle 这一列报“列名无效”或者照着数据字典去建表建完后发现书籍表少了外键约束。原因原文表 2-3 列的是 bookstyle建表脚本用的是 bookstyleno 并带外键引用。两处字段名不一致是报告从数据字典复制到 SQL 章节时没同步导致的。这类问题在课程设计资源里非常普遍数据字典改了一版SQL 没跟着改或者反过来。解决以建表脚本为准把数据字典第 N 列同步成 bookstyleno并注明外键来源。改数据字典比改脚本成本低得多而且外键约束引用的列名必须和父表主键完全一致这是数据库硬性要求没有讨价还价的余地。5.3 insert 笔误导致对象名无效这类错别字用格式化工具一次扫出现象执行初始化数据时报错“对象名 tem_books 无效”或“‘system_booksset’ 不是可识别的表名”但明明表已经建成功了。原因原文脚本里有两处硬伤——insert intotem_books(...)少了 system_ 前缀update system_booksset isborrowed0在表名和 set 之间没有空格。这类错误在人工复制、OCR 扫描、手敲脚本时都容易发生肉眼检查很难看出来因为前缀像 tem_books 会被下意识读成 system_books。解决不要直接执行原始脚本。先做一遍文本替换检查重点看表名前后是否有空格、insert into 是否被粘连、values 是否符合表的列顺序更干脆的做法是把 SQL 粘贴到 SSMS 里利用它的智能提示和红色波浪线或者用格式化工具重排一遍再跑。我的习惯是所有从网上或同学手里拿到的 SQL先格式化、再逐条读表名、最后才执行这一套流程能挡住九成低级语法错误。5.4 时间字符串格式的兼容性补零是最便宜的后悔药现象在 SQL Server 里插2005-9-23 14:23:56能成功换到 MySQL 严格模式、或从 Oracle 迁移脚本时报日期格式错误甚至把数据从 SQL Server 导出再导入时日期变成空值。原因SQL Server 对日期字符串的解析比较宽松月份和日期不补零也能识别但 MySQL 的 sql_mode 开启 strict 后对格式非常挑剔Oracle 更是强制要求明确格式。原报告里的日期写法是手敲的8 月写 89 月写 9不统一。解决统一写成YYYY-MM-DD HH:MM:SS月份和日期不足两位补零。如果你要处理历史数据可以用 cast 或 convert 强制转换比如-- SQL Server 写法显式转换日期字符串避免隐式转换的坑 insert into system_readers(readerid, readername, readersex, readertype, regdate) values(X05620201, 测试, 男, 学生, convert(datetime, 2005-09-23 14:23:56, 120));convert 的第三个参数 120 是 ODBC 标准格式对应 YYYY-MM-DD HH:MM:SS。答辩时如果被问到“日期格式怎么保证跨库兼容”你可以直接答“统一用 ISO 标准格式并显式转换”这个小点很加分。5.5 还书流程没有对应的 update状态字段只进不退现象数据初始化把所有书的 isborrowed 都置成了 0已借出但没有对应的还书脚本。你想模拟还书流程时发现不知道该怎么把书的状态改回 1只能手动执行一条裸 update而且容易漏改。原因原文只提供了借书的 insert update 组合还书模块的需求写了但 SQL 初始化部分没有配套脚本。isborrowed 这个状态字段设计上是“借出为 0、还回为 1”但没人维护它的话整个状态机就是单向的。解决还书时把“插入还书记录”和“更新书状态”也包进一个事务条件反过来begin tran; insert into return_record(bookid, readerid, returndate) values(00125415152, X05620202, 2007-10-27 10:00:00); update system_books set isborrowed 1 -- 还回恢复可借 where bookid 00125415152 and isborrowed 0; if rowcount 0 begin rollback; print 还书失败书原本就不在借出状态; end else commit;这一条的价值在于它把第 4 章那个单方向的借书状态机补全了。借书、还书、罚款三段流程都有对应脚本整个系统才闭环。我去掉那些修饰性描述后这份资源真正缺的就是这段还书事务。6. 验证与升级用查询把流程跑通再补上触发器资源里的 SQL 能不能交差要靠验证而不是靠眼睛看。我每次拿到这类课程设计脚本都会按这个顺序跑一遍验证第一查某本书当前状态第二查某读者借了哪些书没还第三算一笔超期罚款是否合理。对应的 SQL 就三句话-- 验证1这本书在馆还是借出 select bookid, bookname, isborrowed from system_books where bookid 00125415152; -- 验证2某读者当前借了哪些书join 三张表 select r.readername, b.bookname, br.borrowdate from borrow_record br join system_readers r on r.readerid br.readerid join system_books b on b.bookid br.bookid where r.readerid X05620202;超期罚款的计算原文只给了一个记录罚款金额的表但怎么判断“超期”没有写。我用 DATEDIFF 补上并假设学生借期 30 天-- 验证3找出借了超过 30 天还没还的记录 select br.readerid, r.readername, b.bookname, br.borrowdate, datediff(day, br.bookid 00125415152 or 11, getdate()) as borrow_days from borrow_record br join system_readers r on r.readerid br.readerid join system_books b on b.bookid br.bookid where br.bookid not in (select bookid from return_record) and datediff(day, br.borrowdate, getdate()) 30;写完这段我才意识到原报告的问题它把罚款表建出来了但罚款金额从哪来、由谁算全程没有逻辑。课程设计卡在这是常态——表建完就以为系统完成了实际业务逻辑是空的。如果你想把这套资源从“能跑”升级到“能答”最低成本的补强是加一个还书触发器让还书记录一插入就自动把 isborrowed 改回 1彻底消灭手写 update 漏改的可能create trigger trg_return_book on return_record after insert as begin update b set b.isborrowed 1 from system_books b join inserted i on i.bookid b.bookid; end;从那以后我每次验收这类课程设计资源都不再满足于“建表成功、数据插进去了”而是强制自己走一遍完整的借、还、查、罚流程再回头看表和字段设计是否支撑得起这三个动作。这套图书管理系统的表结构、初始化数据可以直接用但主键语义和还书触发器这两处是你说服答辩老师“我真的跑通了”的关键证据。希望帮到你。本文还有配套的精品资源点击获取