简介这份SQL数据库图书管理系统课程设计文档面向高校计算机相关专业学生及数据库初学者用于完成数据库应用技术课程的课程设计任务。文档围绕图书管理系统的完整设计流程展开涵盖系统分析、E-R图绘制、关系模式定义、数据字典编制及SQL语句实现等核心环节帮助读者掌握关系型数据库编程技术与小系统开发方法。资源包内含1个doc文件大小约739KB内容包含设计目标、存储设计指导思想、读者与馆员及管理员三类角色划分、基础数据与业务数据及统计数据说明并给出书籍类别、读者、书籍、借阅、还书、罚款六个关系模式及对应字段设计。目前已有6484人学习下载适合需要参考完整课程设计报告结构、学习E-R图与关系模式转换、借鉴SQL查询与数据操作实现思路的学生可作为课程设计撰写与上机实现的实用范本。1. 从一份 2013 年的 SQL 课程设计说起它到底能帮你省下多少事如果你正在搜「SQL数据库图书管理系统课程设计」大概率是三种人之一数据库课设马上要交、被 E-R 图和关系模式卡住、或者想找一个能跑通的完整案例照着改。这份资源就是一份典型的课程设计报告加源码文档核心用 SQL Server 实现了一个图书管理系统覆盖了从需求分析、E-R 图设计、数据字典、建库建表到数据初始化和单表查询的完整链路。它不花哨但胜在结构完整、SQL 语句可直接执行适合拿来当课设模板或者数据库入门练手项目。我拆过不少类似的课设包这份的 SQL 部分写得比较实在建表和初始化的语句基本能直接跑省去了你自己从零拼建表语句的时间。接下来我会按「它是什么 → 怎么跑起来 → 哪里容易翻车 → 怎么改得更像样」的顺序把这份资源拆开讲清楚。2. 拆开这份课程设计六张表、三个角色和一条完整的借还书链路2.1 系统到底管什么角色划分与数据流这份课程设计把图书管理系统的业务拆成了三个角色读者、图书馆馆员、系统管理员。读者能查自己的借阅记录和罚款信息馆员负责日常的借书还书登记和图书信息维护系统管理员管的是读者账户和权限。这个角色划分虽然简单但已经覆盖了图书管理系统最核心的业务闭环。数据层面分三类基础数据读者信息、图书信息、操作员信息、业务数据借还书记录、罚款记录、统计数据借阅情况统计。基础数据是静态的业务数据是动态增长的统计数据是从前两者算出来的。这个分类思路在课设报告里写清楚答辩的时候老师一般不会再追问「你的数据怎么分类」这种问题。整个系统的数据流是这样的读者先登记基本信息馆员把书籍类别和书籍库存录入然后读者借书时在借阅表里插一条记录、同时把书籍的借出标记改掉还书时在还书表里插记录、恢复借出标记超期的话再往罚款表里插一条。这条链路在报告的数据流程图部分有描述但图比较糊我建议你直接看 SQL 部分更清楚。2.2 六张核心表的关系模式与字段设计报告里给出了六个关系模式对应六张表。我把它们整理成一张对照表方便你快速看清表之间的关系表名中文名主键外键核心字段book_style书籍类别bookstyleno无bookstyleno, bookstylesystem_readers读者信息readerid无readerid, readername, readersex, readertype, regdatesystem_books书籍信息bookidbookstyleno → book_stylebookid, bookname, bookstyleno, bookauthor, bookpub, bookpubdate, bookindate, isborrowedborrow_record借阅记录bookidbookid → system_books, readerid → system_readersbookid, readerid, borrowdatereturn_record还书记录bookidbookid → system_books, readerid → system_readersbookid, readerid, returndatereader_fee罚款记录bookidbookid → system_books, readerid → system_readersreaderid, readername, bookid, bookname, bookfee, borrowdate这张表里有个地方值得注意borrow_record 和 return_record 都把 bookid 设成了主键。这意味着同一本书在借阅表里只能出现一次还书之后如果这本书再次被借就没法往 borrow_record 里插新记录了。这是课设里常见的简化处理实际业务中应该用联合主键或者自增 ID。你在答辩时如果被问到「同一本书能不能被借两次」这就是个坑点后面避坑章节我会展开说。另外 reader_fee 表里 readerid 出现了两次一次作为普通字段、一次作为外键报告原文里写的是「读者借书证编号读者姓名借书证编号书籍编号读者借书时间」这里明显是笔误实际建表语句里 readerid 只出现了一次。以建表语句为准。2.3 建库建表的完整 SQL 与参数解读报告里的建库语句用的是 SQL Server 的语法指定了数据文件和日志文件的路径、初始大小、最大大小和增长率。这段代码可以直接在 SSMS 里执行但路径需要改成你本机的路径-- 创建数据库指定数据文件和日志文件的存储路径与增长参数 CREATE DATABASE librarysystem ON ( NAME librarysystem, FILENAME c:\tangzhangsenlibrary.mdf, -- 改成你自己的路径 SIZE 10, -- 初始大小 10MB MAXSIZE 50, -- 最大 50MB FILEGROWTH 5 -- 每次增长 5MB ) LOG ON ( NAME library, FILENAME c:\tangzhangsenlibrary.ldf, -- 日志文件路径 SIZE 5MB, MAXSIZE 25MB, FILEGROWTH 5MB ) GO这段代码里 SIZE 和 MAXSIZE 的单位如果没写默认是 MBFILEGROWTH 写 5 就是每次涨 5MB。课设数据量小10MB 初始够用了。注意 FILENAME 的路径必须是你机器上真实存在的目录否则会报「操作系统错误 3系统找不到指定的路径」。我一般会先手动建好目录再执行建库语句。建表部分按依赖顺序来先建 book_style 和 system_readers再建 system_books因为它引用了 book_style最后建 borrow_record、return_record 和 reader_fee它们引用了 system_books 和 system_readers。顺序反了会报外键约束错误。-- 书籍类别表类别编号做主键 CREATE TABLE book_style ( bookstyleno varchar(30) PRIMARY KEY, bookstyle varchar(30) ); -- 读者表借书证号做主键 CREATE TABLE system_readers ( readerid varchar(9) PRIMARY KEY, readername varchar(9) NOT NULL, readersex varchar(2) NOT NULL, readertype varchar(10), regdate datetime ); -- 书籍表外键关联书籍类别表 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) );这里有个细节system_readers 的 readername 字段定义的是 varchar(9)但后面插入的数据里有「唐张森」这种三个字的名字9 个字符够用。不过如果遇到四个字的名字比如复姓9 也够。真正要注意的是 readerid 定义成 varchar(9)而插入的数据里 Q20120401 刚好 9 位201005 是 6 位都能存进去。但如果你要改成学号作为借书证号得确认长度够不够。2.4 数据初始化INSERT 与 UPDATE 的配合初始化数据分四步先插书籍类别再插书籍然后插读者最后插借阅记录并同步更新书籍的借出状态。前三步都是单纯的 INSERT第四步需要 INSERT 和 UPDATE 配合-- 插入借阅记录 INSERT INTO borrow_record(bookid, readerid, borrowdate) VALUES(20135678901, Q20120401, 2013-01-18 12:20); -- 同步更新书籍的借出标记为 0已借出 UPDATE system_books SET isborrowed 0 WHERE bookid 20135678901;注意这里 isborrowed 的语义初始插入书籍时全部设为 1在馆借出后改成 0已借出。这个标记逻辑跟直觉是反的很多人第一反应会以为 1 表示借出、0 表示在馆。报告里就是这么定义的你如果自己改建议把语义调过来用 1 表示已借出更符合习惯。但如果你只是照着跑就别动它否则后面的查询结果会对不上。UPDATE 语句后面带了AND isborrowed1的条件这是为了防止重复更新。不过因为 borrow_record 的 bookid 是主键同一本书只能插一次借阅记录所以这个条件加不加效果一样。但养成加条件的习惯是好的避免误更新。3. 把这份课设跑起来从 SSMS 建库到查询验证的完整操作链3.1 环境准备与建库前的检查项这份课设用的是 SQL Server建库语句是 T-SQL 语法。你需要在机器上装好 SQL Server2008 到 2022 都可以语法兼容和 SSMSSQL Server Management Studio。如果你用的是 MySQL建库语句需要改MySQL 不支持ON (NAME..., FILENAME...)这种写法直接CREATE DATABASE librarysystem;就行建表语句里的datetime和varchar也要改成 MySQL 对应的类型。建库之前先确认三件事第一你的登录账户有 CREATE DATABASE 权限sa 或者 sysadmin 角色都行第二FILENAME 指定的目录存在且 SQL Server 服务账户有写入权限第三如果之前已经建过同名数据库先 DROP 掉再重建否则会报「数据库已存在」。我一般会先执行这段检查-- 检查是否已存在同名数据库存在则删除 IF EXISTS (SELECT name FROM sys.databases WHERE name librarysystem) DROP DATABASE librarysystem; GO这段代码在 master 数据库下执行。sys.databases是系统视图查的是当前实例下所有数据库的名字。加IF EXISTS是为了避免第一次执行时报错。3.2 按依赖顺序执行建表与初始化脚本建表顺序不能乱必须按 book_style → system_readers → system_books → borrow_record → return_record → reader_fee 的顺序来。原因很简单system_books 的外键指向 book_styleborrow_record 的外键指向 system_books 和 system_readers。如果先建 borrow_record它会因为找不到 system_books 而报错。初始化数据的顺序同理先插 book_style 和 system_readers再插 system_books最后插 borrow_record 并更新 isborrowed。报告里的 INSERT 语句是逐条写的没有用事务包起来。如果你担心中间出错导致数据不一致可以手动加事务BEGIN TRANSACTION; -- 这里放所有 INSERT 和 UPDATE 语句 -- 如果全部成功 COMMIT; -- 如果中间出错 -- ROLLBACK;课设数据量小不加事务也能跑但养成加事务的习惯对以后做真实项目有好处。3.3 验证数据是否正确的三条查询数据插完之后用三条查询验证查书籍类别、查书籍信息、查借阅记录。报告里只给了前两条我补一条借阅记录的查询-- 查所有书籍类别 SELECT * FROM book_style; -- 查所有书籍按类别编号排序 SELECT * FROM system_books ORDER BY bookstyleno; -- 查借阅记录关联读者姓名和书籍名称 SELECT b.bookid, s.bookname, b.readerid, r.readername, b.borrowdate FROM borrow_record b JOIN system_books s ON b.bookid s.bookid JOIN system_readers r ON b.readerid r.readerid;第三条查询用了 JOIN把借阅记录、书籍和读者三张表串起来。这是课设里比较少见的写法报告里只给了单表查询。但答辩时老师很可能会问「怎么查某个人借了哪些书」这时候就得用 JOIN。JOIN默认是 INNER JOIN只返回三张表里都能匹配上的记录。如果某条借阅记录的 readerid 在读者表里不存在这条记录就不会出现在结果里。3.4 单表查询之外课设报告里没写但答辩常问的 JOIN报告里只演示了SELECT * FROM book_style和SELECT * FROM system_books这种单表查询。但图书管理系统的核心查询几乎都涉及多表关联。除了上面那条借阅记录查询还有几个高频查询你最好提前准备好查某个读者当前借了哪些书还没还SELECT s.bookname, b.borrowdate FROM borrow_record b JOIN system_books s ON b.bookid s.bookid WHERE b.readerid Q20120401 AND b.bookid NOT IN (SELECT bookid FROM return_record);这条查询的逻辑是先从借阅记录里找出该读者的所有借阅再排除掉已经出现在还书表里的书籍。NOT IN子查询在这里能用但如果 return_record 表里有 NULL 值NOT IN会返回空结果。更安全的写法是用NOT EXISTSSELECT s.bookname, b.borrowdate FROM borrow_record b JOIN system_books s ON b.bookid s.bookid WHERE b.readerid Q20120401 AND NOT EXISTS ( SELECT 1 FROM return_record r WHERE r.bookid b.bookid AND r.readerid b.readerid );NOT EXISTS对 NULL 值不敏感是更稳妥的写法。这个知识点在数据库课里通常会讲但课设报告里没体现你如果在答辩时能主动说出来是个加分项。4. 避坑与排查这份课设跑不起来时先看这五条4.1 建库报「操作系统错误 3系统找不到指定的路径」现象执行 CREATE DATABASE 语句时直接报错提示找不到路径。原因FILENAME 里写的c:\tangzhangsenlibrary.mdf这个路径在你的机器上不存在或者 SQL Server 服务账户没有该目录的写入权限。解决先把路径改成你机器上真实存在的目录比如D:\sqlcourse\library.mdf并且确保这个目录已经手动建好了。如果还是报权限错误右键目录 → 属性 → 安全 → 给NT SERVICE\MSSQLSERVER或SQLServerMSSQLUser$你的计算机名$MSSQLSERVER加上写权限。4.2 建表报「外键引用了无效的表」现象执行 CREATE TABLE borrow_record 时报错提示外键引用的表不存在。原因建表顺序不对。borrow_record 引用了 system_books 和 system_readers如果这两张表还没建就会报这个错。解决严格按 book_style → system_readers → system_books → borrow_record → return_record → reader_fee 的顺序执行。如果你已经建了部分表先把它们 DROP 掉再按顺序重建。4.3 插入借阅记录后 isborrowed 字段没更新现象INSERT 借阅记录成功了但查 system_books 发现 isborrowed 还是 1。原因INSERT 和 UPDATE 是两条独立的语句如果只执行了 INSERT 没执行 UPDATE或者 UPDATE 的 WHERE 条件写错了标记就不会变。解决检查 UPDATE 语句的 WHERE 条件是否匹配到了正确的 bookid。报告里的 UPDATE 带了AND isborrowed1如果你插入书籍时 isborrowed 设的是 0这条 UPDATE 就不会生效。确认插入书籍时 isborrowed 的初始值是 1。4.4 同一本书第二次借阅时 INSERT 失败现象读者还书之后同一本书再次被借出时往 borrow_record 插记录报主键冲突。原因borrow_record 表把 bookid 设成了主键这意味着同一本书在借阅表里只能有一条记录。还书之后记录还在再借就插不进去了。解决这是课设设计的简化处理实际业务中应该把 borrow_record 的主键改成自增 ID 或者 (bookid, readerid, borrowdate) 联合主键。如果你不想改表结构可以在还书时把 borrow_record 里的记录删掉但这样会丢失借阅历史。我建议在答辩时主动指出这个问题说明你理解实际业务和课设简化之间的差距。4.5 查询结果中文显示乱码现象SELECT 出来的中文显示成问号或者乱码。原因数据库的排序规则collation不支持中文或者插入数据时客户端编码和数据库编码不一致。解决建库时指定中文排序规则比如CREATE DATABASE librarysystem COLLATE Chinese_PRC_CI_AS;。如果数据库已经建好了可以用ALTER DATABASE librarysystem COLLATE Chinese_PRC_CI_AS;修改。另外确认 SSMS 的查询窗口编码是 UTF-8 或者 GBK跟数据库一致。5. 从能跑到能看三个让课设加分的小改造5.1 把 isborrowed 的语义调过来报告里 isborrowed1 表示在馆、0 表示借出这个语义跟大多数人的直觉相反。我建议你把它调过来1 表示已借出、0 表示在馆。改法很简单把初始化 INSERT 里的 isborrowed 值从 1 改成 0把借书时的 UPDATE 从SET isborrowed0改成SET isborrowed1。这样后面写查询的时候不容易搞混。答辩时如果老师问「你这个字段 1 和 0 分别代表什么」你回答「1 表示已借出」比「1 表示在馆」听起来更自然。5.2 加一个借阅天数计算和超期判断报告里的罚款表只存了罚款金额没有存超期天数。你可以加一个计算列或者视图把借阅天数和超期天数算出来。SQL Server 里可以用 DATEDIFF 函数-- 计算每本借出书籍的已借天数和是否超期假设借期 30 天 SELECT b.bookid, s.bookname, b.borrowdate, DATEDIFF(DAY, b.borrowdate, GETDATE()) AS 已借天数, CASE WHEN DATEDIFF(DAY, b.borrowdate, GETDATE()) 30 THEN 已超期 ELSE 未超期 END AS 超期状态 FROM borrow_record b JOIN system_books s ON b.bookid s.bookid WHERE b.bookid NOT IN (SELECT bookid FROM return_record);DATEDIFF(DAY, 开始日期, 结束日期)返回两个日期之间的天数差。GETDATE()返回当前系统时间。CASE WHEN是条件判断超过 30 天就标记为已超期。这个查询可以直接做成视图答辩演示的时候调出来很直观。5.3 用视图把高频查询封装起来课设报告里全是裸的 SELECT 语句如果你能把几个高频查询封装成视图答辩时看起来会更专业。比如把「当前未归还的借阅记录」做成视图CREATE VIEW v_current_borrow AS SELECT b.bookid, s.bookname, b.readerid, r.readername, b.borrowdate, DATEDIFF(DAY, b.borrowdate, GETDATE()) AS 已借天数 FROM borrow_record b JOIN system_books s ON b.bookid s.bookid JOIN system_readers r ON b.readerid r.readerid WHERE NOT EXISTS ( SELECT 1 FROM return_record rr WHERE rr.bookid b.bookid AND rr.readerid b.readerid );建好之后查当前未归还记录只需要SELECT * FROM v_current_borrow;。视图的好处是把复杂的 JOIN 和子查询藏起来调用方不用关心底层表结构。如果老师问「视图和表的区别」你可以说视图不存数据每次查询时动态生成结果表存实际数据。视图可以简化查询、控制权限但不能直接 INSERT除非满足可更新视图的条件。我当年做课设的时候就是吃了没封视图的亏答辩时老师让我现场写一个「查所有超期未还的书」我写了五分钟的 JOIN 还没写对。后来工作了凡是遇到高频查询第一反应就是先封个视图或者存储过程省得每次现写。希望你做这份课设的时候至少把上面这三个改造过一遍答辩的时候心里有底。希望帮到你。本文还有配套的精品资源点击获取