
简介一份完整的 SQL 数据库图书管理系统课程设计文档面向数据库课程设计者、毕业设计学生及图书管理系统开发学习者用于解决图书借阅、还书、罚款等业务中人工管理混乱、效率低下的问题。文档以广西交通职业技术学院信息工程系的课程设计为依托围绕读者信息、图书信息、操作员信息三大模块完整覆盖书籍类别管理、库存管理、借书还书登记、超期罚款等功能。内容包含数据库存储设计、E-R 实体关系图、数据字典、关系模式、关系代数查询描述及 SQL 实现语句并附有查询结果示例可直接参考其表结构和核心 SQL 语句进行二次开发或撰写报告。压缩包内仅含 1 个 doc 文档大小约 709KB内容为完整的设计报告书含系统设计目标、数据流程图、数据字典表等。目前已有 2317 人学习下载适合需要借鉴完整设计方案、学习数据库课程设计报告写法的读者。文档模块划分明确从问题描述到 SQL 实现步骤完整可帮助理解图书管理信息系统的数据建模与开发流程。1. 翻登记本翻到崩溃这份 SQL 图书管理系统把借阅链路一次打通如果你是图书管理员每天最烦的不是搬书而是查这本书被谁借走了 谁逾期没还。小图书馆还在用 Excel 甚至纸质登记本借阅记录一多查一笔账要翻半天还漏算罚款。这份《SQL 数据库图书管理系统》正好解决这个问题它是一份完整的数据库课程设计从 E-R 图设计、数据字典、关系模式到 SQL 建库建表和初始化数据全部齐全核心目标是让读者、图书、借阅、还书、罚款五类信息统一入库用几条 SQL 就能完成登记、查询、修改和超期罚款计算。适合正在做数据库课程设计的学生也适合想快速搭一个单机图书管理系统练手的一线开发。2. 系统设计与数据库结构六张表如何撑起借还罚全流程2.1 从 E-R 图到关系模式先理清实体再谈建表这是 2013 年的课程设计技术栈是 SQL Server 2000/2005 这一代全文用 T-SQL 语法。它的设计思路非常教科书先做需求分析明确三类角色读者、图书馆馆员、系统管理员再画 E-R 图描述实体关系。项目里有六个核心实体书籍类别、读者、书籍、借阅记录、归还记录、罚款记录。这些实体通过 E-R 图关联之后直接映射成六个关系模式。关系模式如下书籍类别种类编号种类名称读者借书证编号读者姓名读者性别读者种类登记日期书籍书籍编号书籍名称书籍类别书籍作者出版社名称出版日期登记日期借阅借书证编号书籍编号借书时间还书借书证编号书籍编号还书时间罚款借书证编号读者姓名书籍编号书籍名称罚款金额借书时间六个关系模式不是孤立存在的。书籍通过书籍类别关联到 book_style 表借阅和归还通过 readerid、bookid 分别关联读者表和书籍表罚款则通过 readerid 和 bookid 把读者、书籍、借阅时间串起来。这个设计的妙处在于罚款记录不单独存还书时间而是通过 borrowdate借书时间 当前时间比对天然形成了逾期判断的数据基础应用层只需要查这个日期差就能得出罚款金额。2.2 数据字典每张表的字段约束与主外键设计数据字典是这个项目里最有价值的部分之一。它把所有表结构定义成了标准文档格式——列名、数据类型、可否为空、说明四列一套建表之前先定规范。core 表结构设计book_style 书籍类别表bookstyleno varchar(30) 主键bookstyle varchar(30) 种类名称system_readers 读者表readerid varchar(9) 主键注只有 9 位长度说明设计者预期读者数量不大system_books 书籍信息表bookid varchar(20) 主键bookstyleno 作为外键关联 book_styleborrow_record 借阅记录表bookid 和 readerid 都是外键bookid 设为主键return_record 还书记录表与 borrow_record 结构近似reader_fee 罚款表bookid 主键readerid 外键同时冗余了 readername 和 bookname设计上有几点值得玩味。第一借阅记录表把 bookid 设为主键意味着同一本书同时只能有一条借出记录这是一本书不能被两个人同时借走的数据库级约束——这个做法很老派但对单机版管理系统完全够用。第二罚款表冗余了 readername 和 bookname这是典型的反规范化设计省去每次罚款查询都要 JOIN 三张表的开销在 SQL Server 2005 时代是务实之选。2.3 关系图与数据流理解系统如何运转原文的图 2-8 关系图展示了六张表之间的引用关系。从数据流图能看到系统运转链条读者登记 → 借阅信息录入 → 归还信息录入 → 超期罚款。这四步对应了数据库的四次关键写操作读者登记INSERT INTO system_readers写入读者基础信息借书INSERT INTO borrow_record UPDATE system_books SET isborrowed...同步标记书籍借出状态还书INSERT INTO return_record UPDATE system_books SET isborrowed...解除借出状态罚款INSERT INTO reader_fee记录超期罚款信息所以这不是一个只有建表语句的文档而是一个完整的业务闭环。表中还有一个 isborrowed 字段专门记录书籍是否被借出虽然原文的初始化数据把已借出的书置 0这个反直觉的设置后面会专门说但它确实承担了书籍状态的核心职责。3. 核心模块拆解从建库到借还书罚款的 SQL 实现链路3.1 建库与建表T-SQL 里的基础设施系统第一步是创建数据库。原文用的是 SQL Server 的 CREATE DATABASE 语法指定了主数据文件和日志文件的初始大小、最大值和增长步长USE master GO CREATE DATABASE tangzhangsentSG ON ( NAME librarysystem, FILENAME C:\, SIZE 10, MAXSIZE 50, FILEGROWTH 5 ) LOG ON ( NAME library, FILENAME C:\, SIZE 5MB, MAXSIZE 25MB, FILEGROWTH 5MB ) GO这段代码的关键参数有三个SIZE 是初始大小10MB 数据文件MAXSIZE 是文件增长上限50MBFILEGROWTH 是自动增长步长每次增加 5MB。这意味着当数据量超过初始分配后数据库文件会自动扩展不需要人工干预。注意 FILENAME 这里只写了盘符没写具体文件名实际在 SQL Server 2005 运行会报错应该写成类似C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\librarysystem.mdf的完整路径我在复现时把原始路径补齐了这个问题后面避坑章节会细说。建表部分原文用的是标准 T-SQL我精简后保留最核心的外键关系写法-- 书籍类别表父表 CREATE TABLE book_style ( bookstyleno varchar(30) PRIMARY KEY, bookstyle varchar(30) ) -- 书籍信息表子表通过 bookstyleno 关联父表 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), FOREIGN KEY (bookstyleno) REFERENCES book_style(bookstyleno) ) -- 借阅记录表多对多关系的中间表 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) )建表的顺序很重要必须先建父表 book_style再建引用它的 system_books。如果你把顺序反过来SQL Server 会直接报外键引用错误。借阅记录表通过 bookid 和 readerid 两个外键把读者表和书籍表关联起来实现了谁在什么时候借了哪本书的完整记录。所有 varchar 字段都没有加 COLLATE 设置默认按数据库排序规则存中文在 SQL Server 中文版中即简体中文排序。3.2 数据初始化INSERT 与 UPDATE 的状态同步策略建表之后是数据初始化。原文向 book_style、system_books、system_readers、borrow_record 四张表写入了大量示例数据其中最有技巧的是借书时同步更新书籍状态的操作这里用了INSERT UPDATE 配对的方式-- 先插入借阅记录 INSERT INTO borrow_record(bookid, readerid, borrowdate) VALUES(901, Q201201015, 2013-01-18 12:20) -- 同步把这本书标记为已借出 UPDATE system_books SET isborrowed 0 WHERE bookid 901这里必须多说一句原本的初始化数据里isborrowed 0含义其实是已被借出与直觉相反。原因是作者把 isborrowed 当成了是否在馆标记1 代表在馆、0 代表借出而 UPDATE 语句只更新了部分数据。如果按正常逻辑应该写SET isborrowed 1表示已借出。我在复现时统一改成isborrowed 1表示已借出避免后续查询状态时语义混乱。这个细节也是后面踩坑章节的重点。读者数据的 INSERT 也值得看一眼它包含了读者类型字段 readertype学生/教师/管理这个字段后来在罚款和查询时会起到区分角色权限的作用。3.3 借书、还书与罚款业务 SQL 的完整闭环系统最核心的是借书、还书、罚款三个动作。借书流程已经演示过还书流程则是它的逆操作但这里有个容易被忽略的设计还书不是 DELETE 借阅记录而是新增一条还款记录同时更新书籍状态。这样做的好处是历史记录完整可追溯不会因为还书而丢失曾经借过的事实。罚款功能的实现思路是在还书时判断 borrowdate 距今是否超过设定天数如果超了就把读者信息和书籍信息写入 reader_fee 表。-- 还书时发现超期生成罚款记录 INSERT INTO reader_fee(readerid, readername, bookid, bookname, bookfee, borrowdate) SELECT r.readerid, r.readername, b.bookid, b.bookname, 5.00, br.borrowdate FROM borrow_record br JOIN system_readers r ON br.readerid r.readerid JOIN system_books b ON br.bookid b.bookid WHERE br.bookid 901 AND DATEDIFF(day, br.borrowdate, GETDATE()) 30 -- 同时更新书籍为已归还 UPDATE system_books SET isborrowed 0 WHERE bookid 901这段 SQL 第一次出现在这个设计中很有价值它没有硬编码罚款规则而是用DATEDIFF(day, borrowdate, GETDATE())动态计算借阅天数。GETDATE()是 SQL Server 的当前时间函数DATEDIFF(day, 起始日期, 结束日期)返回两个日期间的天数差。读者类型如果能拿到还可以改成按角色阶梯定价——学生每天 0.1 元、教师免费之类的策略这就是二次开发的空间。3.4 核心查询图书列表与借阅明细最后一个核心模块是查询。原文演示了单表查询即SELECT * FROM book_style和SELECT * FROM system_books这是最基础的列表功能。实际管理系统还需要多表联查把读者名字、书名、借书日期拼在一起SELECT r.readername, b.bookname, br.borrowdate, br.readerid FROM borrow_record br JOIN system_readers r ON br.readerid r.readerid JOIN system_books b ON br.bookid b.bookid WHERE br.borrowdate IS NOT NULL ORDER BY br.borrowdate DESC这段 JOIN 查询把读者——借阅——书籍三张表串成一条数据链是图书管理系统最典型的查询语句。ORDER BY br.borrowdate DESC按借书时间倒序最新借阅记录排在最前。如果你想查某个读者借了什么书只需加上WHERE r.readerid 201005即可。4. 避坑指南这份课程设计里最容易翻车的四个细节4.1 数据库文件路径不完整导致建库失败现象按原文执行 CREATE DATABASE 语句时SQL Server 2005 直接报错提示操作无法完成因为 FILENAME 属性不是有效的路径。原因原文写的是FILENAME C:\没带 .mdf 和 .ldf 文件名。SQL Server 要求数据文件必须是完整的物理路径包括文件名和后缀。解决改为完整路径比如FILENAME ND:\Data\librarysystem.mdf。我一般在 C 盘和 D 盘各建一个 Data 目录专门放数据库文件避免和系统盘混在一起导致权限问题。4.2 isborrowed 字段状态含义反转查询结果完全错误现象按原文初始化数据后查询SELECT * FROM system_books WHERE isborrowed 0想查未借出的书结果把已借出的 8 本书全部查出来了。原因原文的 INSERT 语句把所有书的 isborrowed 都置为 1UPDATE 时又改为 0前后语义不一致。字段名 isborrowed 的字面含义是是否被借走但 UPDATE 语句把它当成了是否在馆。解决给字段重新定义约束用 CHECK 约束固定取值语义。我通常改成isborrowed char(1) DEFAULT 0 CHECK (isborrowed IN (0,1))0 代表未借出1 代表已借出并用注释写清楚。且在各处代码中保持一致。4.3 return_record 表列名与说明文档不一致现象看原文数据字典时发现 return_record 表格中第一列写的是 readername但表结构 SQL 里创建的第一列是 readername注释却是读者借阅证编号字段语义对不上。原因课程设计报告是先写文档后改代码导致数据字典和实际表结构不同步。这是学生项目里最常见的文档前后不一致问题。解决以实际 CREATE TABLE 的 T-SQL 为准手工补一份修订后的数据字典。我在复现时把 return_record 表修正为readerid varchar(9) NOT NULL, bookid varchar(20) NOT NULL, returndate datetime主键设为联合主键 PRIMARY KEY (readerid, bookid)避免同一个人重复还同一本书。4.4 借阅表主键唯一约束阻挡重复借阅现象同一个读者借了两本不同的书执行第二条 INSERT 时数据库报违反了 PRIMARY KEY 约束。原因borrow_record 表把 bookid 设为主键一本书同时只能存在一条借阅记录导致同一个读者无法在不同时间借阅不同书籍——因为一旦还书就要删除记录才能再借否则第二次借阅插入就会被主键冲突挡住。解决真正合理的做法是把主键拆成联合主键PRIMARY KEY (readerid, bookid, borrowdate)让同一读者借不同书成为多条独立记录。同时删掉旧借阅记录采用逻辑删除或归档策略保留历史流水方便后期出统计报表。5. 二次开发从课程设计到可上线的管理系统我加了这三板斧5.1 用触发器把罚款计算做成自动化原系统罚款靠手工 INSERT这在真实场景走不通——管理员记不住每个人的借阅日期。我做的第一个改造是把罚款计算做成数据库触发器还书时自动判断是否超期超期自动插入罚款表CREATE TRIGGER trg_calc_fee ON return_record AFTER INSERT AS BEGIN DECLARE readerid varchar(9), bookid varchar(20), borrowdate datetime SELECT readerid i.readerid, bookid i.bookid FROM inserted i SELECT borrowdate borrowdate FROM borrow_record WHERE readerid readerid AND bookid bookid IF DATEDIFF(day, borrowdate, GETDATE()) 30 INSERT INTO reader_fee(readerid, bookid, bookfee, borrowdate) VALUES(readerid, bookid, CONVERT(varchar, (DATEDIFF(day, borrowdate, GETDATE()) - 30) * 0.5), borrowdate) END这段触发器用inserted临时表捕获新插入的还书记录然后从 borrow_record 反查借书日期用DATEDIFF算出超期天数。这样还书动作自动挂上了罚款逻辑管理员只需要专注还书扫描罚款自动生成。5.2 增加一张逾期视图三行 SQL 拿到超期名单原系统没有统计查询馆员想知道现在有哪些书超期没还只能自己翻表。我加了一个视图把借阅记录、读者表、书籍表和当前日期比对直接列出所有超期书籍CREATE VIEW vw_overdue AS SELECT r.readername, b.bookname, br.borrowdate, DATEDIFF(day, br.borrowdate, GETDATE()) AS overdue_days FROM borrow_record br JOIN system_readers r ON br.readerid r.readerid JOIN system_books b ON br.bookid b.bookid WHERE DATEDIFF(day, br.borrowdate, GETDATE()) 30 AND br.bookid NOT IN (SELECT bookid FROM return_record)视图的好处是调用方不用关心多表关联细节管理员直接SELECT * FROM vw_overdue就能工作。NOT IN (SELECT bookid FROM return_record)这一句很关键它筛掉了已经归还的书只保留仍在借而且超期的记录。5.3 补齐体系把单机脚本改成可视化系统骨架原文纯 SQL 脚本只能命令行操作。如果要做成真正的前后端系统我的习惯是保留所有 SQL 逻辑不动外面套一个轻量级桌面壳——C# WinForm 或 Python Tkinter 都行用 ODBC 连接 SQL Server借书按钮执行存储过程还书按钮触发触发器查询界面直接跑视图。改造时最重要的是别改表结构表字段如果有变动所有历史 SQL 都要重新验证。给一张改造对照表参考模块原SQL方式改造后方式优先度建库建表手工脚本保留原脚本增量迁移脚本高借书INSERT语句封装为存储过程 usp_borrow_book高还书罚款手工INSERT触发器自动计算罚款表联动中查询统计SELECT语句视图报表页高从这段经历里我最深的教训是课程设计里的数据库结构往往能跑但扛不住真实业务每张表的字段约束和主外键关系不能只为了交作业要提前加索引、触发器、约束。现在每次接手这种学生项目我都会先检查主键是否有语义冲突、状态字段是否符合直觉、外键有没有阻止操作失控——这三步走完项目才算真正落地。希望这份 SQL 图书管理系统拆解能帮你在二次开发时少走几段弯路。本文还有配套的精品资源点击获取