ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

图书馆借阅管理数据库设计全流程:从ER图到SQL Server三范式落地

图书馆借阅管理数据库设计全流程:从ER图到SQL Server三范式落地 简介一份面向图书馆借阅管理场景的数据库课程设计文档适用于高校数据库课程设计、期末作业以及SQL初学者学习参考。文档从需求分析入手清晰列出了书籍信息表、借阅信息表、出版社信息表和借阅关系表的核心字段涵盖图书查询、借还记录、出版社增购等多类业务同时包含ER图绘制、关系模型转换以及第三范式规范化分析完整呈现了关系数据库从概念模型到表结构落地的设计流程。资源为单个doc文件压缩包大小1.91MB排版完整已有252人次浏览学习。借助该文档读者既能掌握图书馆借阅管理系统的数据库设计方法也能了解SQL Server 2012与Visual Studio 2012环境下的实现技术并参考其中的系统测试、备份维护等说明完善课程设计报告对完成类似信息管理系统的开发有直接参考价值。1. 图书馆借阅管理数据库设计一份能让你少熬两个通宵的课程设计全流程这份数据库SQL图书馆借阅管理数据库设计文档把数据库课程设计的完整链路走了一遍从需求分析开始画ER图按四条规则把ER模型转成关系模型逐个关系做范式判定并规范化到第三范式最后在SQL Server里建库建表、录数据、跑查询验证。它值钱的地方不是某一段能直接抄的建表SQL而是「从一句需求描述推到表结构」的中间过程——比如怎么从「任何人可借多种书任何一种书可为多个人所借」判断出借阅是M:N联系进而拆出一张独立的借阅表。适合正在赶课程设计报告的学生也适合想快速搭一套规范图书借阅数据模型的新手。我按SQL Server 2012环境完整复现过一遍脚本写法兼容2008及以上版本拿回去改改库名就能用。2. 从需求到ER图图书、出版社、读者三类实体怎么一次画对2.1 从需求描述里拆实体和属性原始需求一共三句话对应的服务目标分别是「查库存」「查借还」「联系出版社增购」。ER图建模的第一步不是画框而是把这三句话里的名词和量词摘出来。第一句出现了书号、书籍种类、数量、存放位置并明确「书号唯一标识」这是图书实体第二句出现了借书人单位、姓名、借书证号、借书日期、还书日期这里其实藏了两个东西——借书人是读者实体借书日期和还书日期不是任何实体的固有属性而是「借阅」这个联系发生时产生的数据第三句的出版社名、电话、邮编、地址构成出版社实体「出版社名具有唯一性」直接就是候选键。这里有一个新手最常见的分歧点要不要把出版社做成实体。如果出版社只有名字一个属性塞进图书表当普通列就行但现在出版社要记录电话、邮编、地址而且一个出版社出版多种书如果把这些信息当成图书的属性同一家出版社的电话会在它出版的每本书上重复一遍冗余立刻失控。判断实体和属性的经验法则很简单这个对象有没有独立于宿主实体的属性需要记录它会不会被多个实例共享两个答案都是「是」就拆成实体。三个实体的属性与主键如下表实体属性主键图书书号、书名、种类、数量、存放位置、出版日期书号读者借书证号、姓名、单位借书证号出版社出版社名、电话、邮编、地址出版社名注意我把出版日期列在了图书侧。严格说它属于「出版社出版了这本书」这个联系但在1:N联系里联系属性可以随N端实体一起并入所以放进图书表是标准做法。而借书日期、还书日期必须挂在借阅联系上——这是后面关系模式转换的关键先记住这一点。2.2 画ER图两个联系、三种基数怎么表达实体定了之后画ER图要表达的是实体之间的关系。本设计有两个联系出版出版社与图书之间是1:N。一个出版社出版多种书同一本书只属于一个出版社。ER图上靠近出版社的一端标1靠近图书的一端标N。借阅读者与图书之间是M:N。「任何人可借多种书任何一种书可为多个人所借」就是典型的M:N语义。ER图上读者端标M图书端标N中间菱形框写「借阅」借书日期、还书日期挂在菱形框上。画图时菱形联系要单独拉出来不要在实体之间乱连线。M:N联系是后面最容易出错的地方——它不能像1:N那样并入某一端必须独立转成一张表。用Visio或draw.io画图时建议把联系属性单独放在菱形旁边不要并进任何一个实体矩形否则转关系模式时会漏字段。说一个当时我们反复确认的细节这张ER图里没有三元联系。有同学会在读者、图书、出版社之间硬画一个三元联系比如「某个出版社的某本书被某个读者借走」这反而会把问题复杂化——出版社信息完全可以通过图书表拿到它不参与借阅关系。ER图的原则是能少画一个菱形就少画一个实体少、联系清楚后面每个环节都省事。2.3 ER图检查清单交图前过一遍画完ER图我会按这几项自查每个实体都有主键。图书用书号、读者用借书证号、出版社用出版社名三处唯一性需求全部对上。联系的必要属性没有遗漏。借阅缺了还书日期后面查借还情况就缺数据出版缺了出版日期增购时没法判断版本新旧。M:N联系确认独立成表。只要图上出现M:N就默认它要变成一张新表不要试图塞进实体表。检查时最容易被忽略的是「借书证号具有唯一性」和「出版社名具有唯一性」这两条约束。它们在ER图上对应实体的候选键在关系模式里会变成UNIQUE约束建表时如果漏掉课程设计报告里的「完整性设计」环节会被扣分。我习惯在ER图每个实体下方把主键和候选键都标出来这样转关系模式的时候约束不会丢。3. 关系模式与范式判定借阅表为什么要拆到第三范式3.1 ER转关系模型的四条原则任务书里明确要求写出从ERD导出关系模型的四条原则——真到了实现阶段会发现这几条就是建表结构的全部依据一个实体转换成一个关系模式实体属性转为关系属性实体的码就是关系的主键。1:1联系并入任意一端把另一端的主键作为外键加进来。1:N联系并入N端把1端的主键作为外键加到N端。M:N联系独立转换成一个关系模式两端实体主键组合起来联系自身的属性也一并放入。按这四条规则本设计得到四个初始关系模式下划线标主键出版社出版社名电话邮编地址图书书号书名种类数量存放位置出版社名出版日期读者借书证号姓名单位借阅借书证号书号借书日期还书日期借阅关系的主键是借书证号书号复合主键借书日期和还书日期作为联系属性只能放在这张表里。图书表里出现出版社名是因为1:N联系并入了N端出版社名在这里是外键。3.2 函数依赖与范式判定先找依赖再看主键范式判定是报告的重头戏要逐个关系说明达到第几范式。判定顺序是先找函数依赖再看有没有部分依赖和传递依赖。以借阅关系为例如果初始设计不小心变成了借阅借书证号书号借书人姓名借书人单位借书日期还书日期函数依赖分析借书证号 → 借书人姓名借书人单位借书证号书号→ 借书日期还书日期。借书日期和还书日期完全依赖于整个复合主键没问题但借书人姓名、单位只依赖于主键的一部分借书证号这就是部分函数依赖。存在部分函数依赖意味着该关系只满足1NF、不满足2NF必须做规范化把部分依赖的属性拆出去形成读者借书证号姓名单位和借阅借书证号书号借书日期还书日期两个关系。再看图书关系。主键是书号单属性主键不存在部分依赖至少是2NF。再检查传递依赖书号 → 出版社名。如果出版社的电话、邮编、地址也被塞进图书表那么书号 → 出版社名 → 出版社电话就构成传递依赖不满足3NF。这正是出版社必须单独建表的原因——把传递依赖链从图书关系中切断。规范化之后图书表只保留出版社名作为外键出版社的详细信息回到出版社表存储一条数据只存一份。3.3 规范化后的最终关系模式经过上述两步四个关系全部满足第三范式关系模式主键外键范式出版社出版社名电话邮编地址出版社名无3NF图书书号书名种类数量存放位置出版社名出版日期书号出版社名3NF读者借书证号姓名单位借书证号无3NF借阅借书证号书号借书日期还书日期借书证号书号借书证号、书号3NF这里有一个值得写进报告的分析借阅关系的复合主键借书证号书号在语义上禁止同一个读者重复借同一本书。但现实中读者还书之后完全可以再借一次如果把这个语义当成硬性约束这张表就只记录一次借阅历史。这个问题在理论分析阶段不用处理按复合主键写报告就行到SQL Server建表时我一般会加一个代理主键BorrowID这是下一章的内容。4. 在SQL Server里落地建表DDL脚本、外键与索引一次写对4.1 建库与出版社表、图书表开发环境我用的SQL Server 2012原题写的是SQL Server 2005。脚本里用到的语法DATE类型、IDENTITY自增、GO批处理从2008开始都支持2005用户需要把DATE换成DATETIME新装的2019、2022也直接兼容不需要额外改动。理论模型和物理实现之间有一道绕不开的坎理论主键和代理主键。上一章确定的四个关系模式里出版社以出版社名为键、借阅以借书证号书号组合为键。到落地时我习惯加一个无业务含义的IDENTITY主键PublisherID、BorrowID原因是出版社名在实际业务里可能变更改名、合并用它做外键牵一发动全身组合主键则会让同一读者重复借同一本书的历史记录直接冲突。代理主键加上后再用UNIQUE约束把出版社名、借书证号这些业务唯一键补上语义约束一条不少。建库和出版社表CREATE DATABASE LibraryDB; -- 创建数据库 GO USE LibraryDB; -- 切换到当前库 GO -- 出版社表 CREATE TABLE Publisher ( PublisherID INT IDENTITY(1,1) PRIMARY KEY, -- 代理主键自增 PublisherName NVARCHAR(50) NOT NULL UNIQUE, -- 出版社名唯一 Phone NVARCHAR(20) NOT NULL, -- 联系电话 ZipCode CHAR(6) NOT NULL, -- 邮编定长6位 [Address] NVARCHAR(100) NOT NULL -- 地址 );INT IDENTITY(1,1) 从1开始每次加1不用手工维护编号NVARCHAR 用来处理中文长度按实际业务取50或100邮编这种定长数据用 CHAR(6) 比 VARCHAR 查询效率高[Address] 加方括号是防止字段名与个别版本的关键字解析冲突这是SQL Server的常规写法。图书表-- 图书表 CREATE TABLE Book ( BookID INT IDENTITY(1000,1) PRIMARY KEY, -- 书号从1000开始编号 BookName NVARCHAR(100) NOT NULL, -- 书名 Author NVARCHAR(50) NOT NULL, -- 作者 Category NVARCHAR(30) NOT NULL, -- 种类如数据库/网络/文学 PublisherID INT NOT NULL, -- 所属出版社外键 PublishDate DATE, -- 出版日期 StockQuantity INT NOT NULL DEFAULT 0, -- 库存数量 [Location] NVARCHAR(50), -- 存放位置如 A-01-03 CONSTRAINT FK_Book_Publisher FOREIGN KEY (PublisherID) REFERENCES Publisher(PublisherID), -- 参照完整性 CONSTRAINT CK_Book_Stock CHECK (StockQuantity 0) -- 库存不得为负 );出版日期和出版社归属在这里落定出版日期是「出版」联系的属性按1:N并入N端表PublisherID 作为外键指向出版社表保证数据库里不会出现「挂在不存在出版社下的书」。库存加 CHECK 约束防止负数录入时手误输成-5就会被拦下。4.2 读者表和借阅表外键与状态设计读者表结构简单借书证号用定长CHAR(10)做主键-- 读者表 CREATE TABLE Reader ( CardID CHAR(10) PRIMARY KEY, -- 借书证号定长10位 ReaderName NVARCHAR(20) NOT NULL, -- 姓名 Unit NVARCHAR(100) NOT NULL -- 单位 );借阅表是本设计的核心表几条约束都集中在这里-- 借阅表 CREATE TABLE Borrow ( BorrowID INT IDENTITY(1,1) PRIMARY KEY, -- 借阅记录号代理主键 CardID CHAR(10) NOT NULL, -- 借书证号 BookID INT NOT NULL, -- 书号 BorrowDate DATE NOT NULL DEFAULT CAST(GETDATE() AS DATE), -- 借书日期默认当天 ReturnDate DATE, -- 还书日期未还时为NULL BorrowStatus CHAR(1) NOT NULL DEFAULT 0, -- 0借出 1已还 CONSTRAINT FK_Borrow_Reader FOREIGN KEY (CardID) REFERENCES Reader(CardID), CONSTRAINT FK_Borrow_Book FOREIGN KEY (BookID) REFERENCES Book(BookID), CONSTRAINT CK_Borrow_Status CHECK (BorrowStatus IN (0,1)), CONSTRAINT CK_Borrow_Return CHECK (ReturnDate IS NULL OR ReturnDate BorrowDate) );几个设计点逐个说。ReturnDate 允许NULLNULL天然表示「还没还」这是借还业务里最自然的建模方式不需要额外造一个9999-12-31之类的特殊日期。BorrowStatus 用 CHAR(1) 存0/1而不是存中文借出/已还是为了避免中文状态下的全角空格、大小写等脏数据展示层需要时再用CASE转换。最后一条 CHECK 约束保证还书日期不早于借书日期——这是数据录入里最常被忽略的脏数据来源同学之间互相抄数据时经常出现。另外我一般会加一条唯一约束防止同一个人同一天重复借同一本书ALTER TABLE Borrow ADD CONSTRAINT UQ_Borrow_Once UNIQUE (CardID, BookID, BorrowDate);这条是可选优化课程设计报告里提一句能加分不提也不影响主要流程。4.3 完整性约束汇总与索引设计四张表的完整性设计汇总如下完整性类型约束手段具体位置实体完整性PRIMARY KEYPublisherID / BookID / CardID / BorrowID参照完整性FOREIGN KEYBook.PublisherID、Borrow.CardID、Borrow.BookID用户定义完整性UNIQUEPublisherName、CardID、CardIDBookIDBorrowDate用户定义完整性CHECK库存≥0、状态∈{0,1}、还书日期≥借书日期用户定义完整性DEFAULT库存默认0、状态默认0、借书日期默认当天索引方面主键自带聚集索引另外再按查询路径补三个非聚集索引-- 索引设计 CREATE INDEX IX_Borrow_CardID ON Borrow(CardID); -- 按借书证号查借还记录 CREATE INDEX IX_Borrow_BookID ON Borrow(BookID); -- 按书号查某本书被谁借走 CREATE INDEX IX_Book_Category ON Book(Category); -- 按种类统计库存借阅表的两个索引覆盖的就是本设计的第一、二项服务目标。借阅表是典型的读多写少、以查询为中心的表数据量涨到几十万行后JOIN查询没有索引很容易变成慢SQLBook表的种类索引服务按种类分组统计库存的报表类查询。索引也不是越多越好——每个索引都会增加写入开销课程设计场景下这三条足够报告里「数据库保护设计」一节可以拿这个取舍展开讲。5. 避坑与常见问题课程设计最容易翻车的五个现场5.1 借阅表直接塞借书人姓名和单位现象为了查询方便把借书人姓名、单位直接写进借阅表甚至不单独建读者表借书证号、姓名、单位全堆在一张表里。老师问「这张表满足第几范式」答不上来。原因ER图阶段把读者实体的属性错误挂在了借阅联系上转换关系模式时没有按第四条原则拆出独立的关系。部分函数依赖存在连2NF都不满足报告里范式分析部分直接失分。解决回到ER图把读者实体独立出来借阅表只保留借书证号作为外键。查询需要姓名和单位就JOIN Reader表一条JOIN的事换来的是规范化报告的完整得分。这个坑几乎每个班都会有人踩我后来批过几次类似的设计基本都是同一个原因。5.2 从Word复制的建表脚本报语法错误现象从Word文档里把建表脚本粘到SSMS报一堆语法错误仔细一看表名或字段名带上了全角方括号或者被Word的智能引号替换成了中文引号。原因课程设计文档常用中文标识符比如[图书]、[书号]SQL Server里必须用半角方括号包裹从Word复制时中英文标点混用SSMS只认半角字符中文引号直接解析失败。解决我一般直接改用英文标识符Book、Reader、Borrow彻底避开标点坑。如果报告要求贴中文表名就在SSMS里手工重敲方括号不要从Word直接粘贴脚本和报告里的截图可以分开处理脚本用英文名不影响成绩。5.3 删数据时外键冲突现象写完插入脚本想清空重来执行 DELETE FROM Publisher 直接报错提示 DELETE statement conflicted with the REFERENCE constraint。原因Book 表通过外键引用 PublisherBorrow 表又同时引用 Book 和 Reader。删父表数据时子表还有引用它的行外键约束直接把删除拦下来。这是数据库保护机制在正常工作不是Bug。解决按子表到父表的顺序删先删 Borrow再删 Book最后删 Publisher。或者建外键时用 ON DELETE CASCADE但课程设计阶段我不建议用级联删除——报告里讲参照完整性时SQL Server默认的NO ACTION行为更容易解释清楚也更能体现你理解外键的工作机制。5.4 还书日期为NULL的查询陷阱现象想查「还没还的书」写 WHERE ReturnDate NULL结果一条都查不出来。换了很多写法都一样。原因NULL在SQL里表示未知值任何与NULL的比较运算结果都是UNKNOWNWHERE条件不成立。这是数据库课程必考的常识也是实际开发里最常见的NULL翻车现场连工作几年的同事偶尔也会踩。解决用 IS NULL 判断写成 WHERE ReturnDate IS NULL。更稳妥的做法是同时查 BorrowStatusWHERE BorrowStatus 0状态字段永远不为NULL用它做主判断ReturnDate 只做参考。两条条件一起用查询结果不会因为漏录还书日期而失真。5.5 每个表15条记录的要求凑不齐现象任务书要求每张表至少15条记录结果图书只有8本借阅表想凑15条就出现同一本书被同一个人反复借的奇怪数据一查就露馅。原因录入前没规划数据量。出版社、图书、读者这类基础表相对好凑借阅表是借书证号和书号的组合基础表基数不够就只能硬编硬编出来的数据要么重复要么逻辑矛盾。解决先保证基础表有足够冗余——出版社5家、图书15本以上、读者15人借阅表取读者和图书的组合想凑多少条都行。插入时可以用随机组合快速生成但课程设计报告里最好用肉眼可读的显式INSERT方便老师检查也方便自己验证。6. 测试数据与查询验证用15条记录把三项目标跑通6.1 录入顺序与测试数据插入顺序必须是 Publisher → Book → Reader → Borrow这个顺序由外键依赖关系决定。给一组可用的出版社数据INSERT INTO Publisher (PublisherName, Phone, ZipCode, [Address]) VALUES (N高等教育出版社, N010-58581118, N100029, N北京市西城区德外大街4号), (N机械工业出版社, N010-88379833, N100037, N北京市西城区百万庄大街22号), (N清华大学出版社, N010-62770175, N100084, N北京市海淀区双清路学研大厦), (N人民邮电出版社, N010-81055656, N100164, N北京市丰台区成寿寺路11号), (N电子工业出版社, N010-88254888, N100036, N北京市海淀区万寿路173信箱);图书和读者按同样格式各写15条以上书名、作者、种类分布在两三个类别里方便后面统计。借阅表取读者和图书的组合造15到20条其中留两三条 ReturnDate 为NULL、BorrowStatus为0的数据专门用来验证未还书查询。6.2 三个目标查询的验证脚本需求里的三句话对应三条验证查询。第一条查书库现有书籍的种类、数量与存放位置SELECT Category AS 种类, COUNT(*) AS 种数, SUM(StockQuantity) AS 总册数, [Location] AS 存放位置 FROM Book GROUP BY Category, [Location] ORDER BY Category;第二条查书籍借还情况包括借书人单位、姓名、借书证号、借书日期和还书日期SELECT r.CardID AS 借书证号, r.ReaderName AS 姓名, r.Unit AS 单位, b.BookName AS 书名, br.BorrowDate AS 借书日期, br.ReturnDate AS 还书日期 FROM Borrow br JOIN Reader r ON br.CardID r.CardID JOIN Book b ON br.BookID b.BookID ORDER BY br.BorrowDate DESC;第三条当库存不足时联系出版社增购SELECT b.BookName AS 书名, b.StockQuantity AS 当前库存, p.PublisherName AS 出版社, p.Phone AS 电话, p.[Address] AS 地址 FROM Book b JOIN Publisher p ON b.PublisherID p.PublisherID WHERE b.StockQuantity 2;6.3 验证方法这三条查询全部跑通、返回的数据和手工核对一致这份设计才算真正落地。从那以后我每次拿到课程设计文档都强制走一遍这个流程先把需求里的每个动词列成一条可执行查询再把对应的数据录进去最后用查询结果反推表结构对不对。需求三句话对应三条查询全绿了报告里的每个承诺才算是兑现了——而不是只在文档里写「可以实现」。希望帮到你。本文还有配套的精品资源点击获取
返回列表