ARTICLE DETAIL

资讯详情

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

图书管理系统E-R图与数据库设计:从实体联系到建表SQL全拆解

图书管理系统E-R图与数据库设计:从实体联系到建表SQL全拆解 简介围绕图书管理系统的数据库设计这份PDF完整给出实体联系图、数据流图与关系模式并附有数据库字段定义说明适合数据库课程设计、考试复习或软件工程文档撰写参考。内容覆盖管理员、读者、书籍、书架、阅览室等核心实体以及读者借阅表、归还表和图书类型等字典表逐项列出字段名称、数据类型、取值范围、主外键约束与是否为空同时梳理读者办证、信息验证、借书、还书、缴纳欠费等环节的数据流向帮助读者从概念模型到关系模式落地形成完整理解。整个资源包内含单个PDF文档大小约332KB无需解压即可打开查看。已有2718人浏览学习适合正在准备数据库考试、完成课程设计或编写需求文档的学习者字段定义说明还可直接作为数据字典编写依据。1. 图书管理系统E-R图PDF为什么课程设计和数据库考试都绕不开它上周末帮一个学弟看数据库课程设计他把图书管理系统的E-R图画成了“一张大网”读者和书籍之间直接连线中间没有借阅表读者类型也当普通字段塞在读者表里。我翻出这份PDF对着改了一个小时发现文档里其实早把答案写清楚了。它不是一张孤立的图而是E-R图、数据流图、关系模式、字段定义表四部分组成的完整设计材料覆盖了“概念模型→逻辑模型→物理表定义”的整条链路。对三类人最有用正在赶数据库课程设计的学生备考数据库期末考试的人以及接了个“做一个简单图书管理系统”需求但建表没头绪的开发者。看到最后你会明白这份PDF值钱的地方不只是图而是那些连主键、默认值都标好的字段定义。2. 拆解E-R图实体、属性、联系这三层决定你的表结构拿到PDF先别急着看关系模式从E-R图这一层开始梳理。实体是名词联系是动词属性是名词的修饰。图书管理系统里这几样分得很清楚管理员操作业务读者产生借还行为书籍和某类书籍提供被借阅的对象书架和阅览室描述存放位置读者类型规定权限书籍类型负责分类。只要这一层不乱后面建表就照抄不会跑偏。2.1 实体清单与主键候选为什么“某类书籍”和“书籍”要拆开文档开头列出的实体很有代表性管理员、账号信息、读者、读者类型、某类书籍、书籍、书籍类型、书架、阅览室。这里最容易被初学者忽略的是“某类书籍”和“书籍”的拆分。某类书籍以ISBN为主键对应“一本书”比如《数据库系统概论》第五版书籍以条形码为主键对应“一册书”比如图书馆里那本贴了条码的、可被借走的物理副本。同一个ISBN可以对应好几本副本这就是“库存量”和“在馆数量”挂在某类书籍上的原因。如果把书名、作者、出版社也塞进“书籍”表每一册书都要重复存一遍书名改版或下架时还要批量改所有副本数据冗余非常难看。反过来如果把“在馆数量”挂到单本上一次借书就要同时改两个表的数量逻辑更乱。文档把这两层拆开是在用实体区分“书目信息”和“馆藏信息”。我在实际项目中见过很多翻车案例都是因为嫌表多把这两张表合并成一张最后做统计时要么重复计算要么丢了副本状态。主键也要单独说一嘴读者账号和账号信息的关系是1:1文档把登录账号单独放一张“账号信息”表读者表再通过外键引用它这样做的好处是登录认证和业务资料解耦。考试时如果让你补全E-R图记得把“账号信息”画成独立实体或者至少标注成读者实体的弱实体否则评卷老师会认为你没理解权限和业务分离。2.2 联系与基数借阅、归还、属于、位于、拥有怎么画才不丢外键E-R图里最核心的其实是联系及基数。文档中出现的联系可以归纳成下面这张表每条都对应一个外键或者一张中间表联系实体A实体B基数转换规则管理管理员读者、书籍、读者类型、书籍类型1:N在N端加外键拥有读者读者类型N:1在读者表加读者类型外键属于书籍某类书籍N:1在书籍表加ISBN外键归类某类书籍书籍类型N:1在某类书籍表加书籍类型编号外键位于书架阅览室N:1在书架表加阅览室编号外键借阅读者书籍M:N拆成读者借阅表归还读者书籍M:N拆成读者归还表借阅和归还是两张独立的联系不是一张表的两列。文档里分别定义了“读者借阅表”和“读者归还表”原因很直接借阅表记录当前还没有归还的借出动作归还表则保存历史记录。如果只保留一张借阅表把实际归还日期加进去那查询“当前谁借了这本书”就要额外过滤归还日期是否为空而拆成两张表后借阅表天然就是“在借列表”查询和统计都更顺手。考试时题目经常问“图书借阅是几对几联系”标准答案就是读者和书籍之间的多对多必须拆中间表。我画E-R图时的习惯是先把实体的主键属性标记好再把联系菱形画在中间连线上标1和N最后才补其他属性。原因很简单主键决定外键外键决定表结构。如果你一开始就纠结“邮箱要不要是唯一键”“余额用什么类型”反而会把基数关系漏掉。2.3 画E-R图的落地顺序先实体、再联系、最后补属性画一张能直接转成关系模式的E-R图我一般按四步走。第一步画实体矩形管理员、读者、某类书籍、书籍、书架、阅览室、书籍类型、读者类型一共八个实体。第二步画菱形联系把借阅、归还、管理、拥有、属于、位于、归类七组联系都放上去并在连线上标注基数。注意“管理员”和“读者类型”之间也有管理联系考试题里容易漏。第三步补属性椭圆从文档的字段定义表往回抄每个实体加主键和必要属性联系上标出它们自身的属性比如借阅日期、续借次数、实际归还日期。第四步检查每个联系是否都能在关系模式阶段找到落脚点——1:N联系在N端加外键M:N联系拆表1:1联系任选一端加外键。这里有个容易踩的细节书架和阅览室之间的联系“位于”在文档里是用书架表外键指向阅览室编号来表示的这没问题。但书架和书籍之间还有一种物理摆放关系文档里书架表还放了“条形码”字段说明它试图记录“哪些书放在这个书架上”。这个设计有一定语义歧义我在后面第5章会单独说画图阶段先知道这两个方向都存在即可。画E-R图不需要一次性画得漂亮很多同学习惯在电脑上直接用Visio或draw.io画但我建议先手写在草稿纸上把实体和联系的编号列出来确认没有漏联系再上工具否则改来改去效率很低。3. 关系模式与字段字典把图转成建表SQL字段级细节全在这E-R图是概念模型关系模式是逻辑模型字段定义表则是物理模型的原材料。这份PDF把关系模式和字段定义放在一起省去了“自己推导主外键”的中间步骤。你需要做的是从这些文字里提取出可执行的建表语句并且理解每一个字段为什么这样设计。这一章我一边解释文档给出的关系模式一边给出对应的MySQL写法你照着改就能用。3.1 关系模式清单主键、外键、唯一键逐条对应文档给出的核心关系模式可以整理成下面这份清单管理员管理员账号姓名性别住址账号信息账号密码账号类型读者读者账号读者类型是否可用姓名性别系别班级邮箱余额读者类型读者类型借书上限借书最大时间最大续借次数某类书籍ISBN书名作者主题出版社页数价格书籍类型编号出版日期库存量在馆数量书籍条形码ISBN书架编号书籍状态损坏程度书籍类型书籍类型编号书籍类型名称书架书架编号条形码阅览室编号阅览室阅览室编号阅览室名称阅览室位置读者借阅表读者账号条形码借出日期续借次数读者归还表读者账号条形码借出日期实际归还日期续借次数把主键、外键标出来以后再读这份清单能看到几条隐藏规则。第一读者表的主键“读者账号”同时是外键指向账号信息表说明读者必须先有登录账号才能建立业务档案。第二书籍表的主键“条形码”之外ISBN是外键指向某类书籍书架编号是外键指向书架这相当于把“哪本书”“放在哪”“现在什么状态”三个维度都挂在单本记录上。第三借阅表和归还表的主键都用读者账号加条形码这是多对多关系拆出来的中间表。有同学问我为什么归还表不直接把“借出日期”和“实际归还日期”都放在借阅表里更新这是典型的课程设计疑问。把归还信息单独拆表除了能保留历史还能避免借阅表主键语义被破坏。借阅表里一条记录代表“一次尚未结束的借阅”如果归还了还留在表里那么“查当前在借书籍”就必须加WHERE实际归还日期IS NULL索引和查询条件都会变复杂。拆表之后借阅表管状态归还表管历史各司其职。3.2 字段定义表拆解数据类型、取值范围、默认值背后的取舍PDF后半部分的字段定义表才是精华所在。它把每个表的字段、类型、范围、默认值、是否为空都列了出来。我挑几个有代表性的字段说说背后的取舍。先看读者信息表“是否可用”字段定义为CHAR(1)取值范围“0”和“1”默认值“0”其中“0”为可用。很多初学者看到这里会问直接用TINYINT或BOOL不好吗在MySQL里确实可以用TINYINT但文档的写法是典型的课程设计风格为的是在评审时一眼能看出字段含义。如果你不打算用ORM框架这个CHAR(1)的取值方式在Java或PHP里做判断也很直观if (0.equals(reader.getAvailable()))。再看“余额”字段用Smallint这是文档里一个值得商榷的地方。如果余额按“元”存Smallint最大32767正常读者余额够用但如果按“分”存也能存327元。实际建表时我更建议改成DECIMAL(10,2)方便记录带小数的欠费金额这个属于从文档迁移到实际数据库时可以做的小改进。读者类型表里的“借书最大时间”字段文档标注“单位是月”默认值2借书上限默认6最大续借次数默认2。这三个Tinyint字段的取值组合决定了后续借书业务里“是否有资格借书”“是否能续借”的判断逻辑。书籍表里“损坏程度”用Tinyint取值范围0到40是无损坏4是无法使用。这种“0-4分档”的设计比纯文字枚举更规范但如果你在MySQL里想加约束可以用CHECK或注释说明后面我会给出SQL写法。还有一个容易被忽略的细节文档某些表名有复制粘贴错误比如某类书籍的关系模式被写成“某类书籍ReaderType”字段定义表也出现了“读者类型ReaderType”的串名。实际这里应该是“BookInfo”才符合语义。这不是什么大问题但你在照着写文档时要能识别出来否则建表时把表名抄错后面所有外键都会跟着错。3.3 生成MySQL建表语句主外键、Check约束、默认值一次到位把文档转成MySQL建表语句我通常按依赖顺序分四组执行。第一组是最基础的公共表管理员和账号信息。CREATE TABLE Administrator ( AdminId CHAR(15) PRIMARY KEY, AName CHAR(8) NOT NULL, ASex CHAR(2) NOT NULL CHECK (ASex IN (男, 女)), APhone CHAR(11) NOT NULL, AAddress TEXT NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE UserMessage ( UserName CHAR(15) PRIMARY KEY, PassWord CHAR(20) NOT NULL, UserType CHAR(6) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里把“管理员”和“账号信息”拆成两张表是因为管理员也需要登录账号但管理员表存的是管理员自身资料账号表存的是认证信息。CHAR类型比较适合定长字段姓名和电话这种长度稳定的字段用CHAR比VARCHAR好磁盘IO略优AAddress用TEXT是考虑到住址长度不稳定。ENGINEInnoDB是事务和行锁的基础字符集用utf8mb4是为了中文和生僻字都能存。第二组是读者相关表注意外键的引用顺序必须先建读者类型再建读者。CREATE TABLE ReaderType ( ReaderType CHAR(4) PRIMARY KEY, MaxBorNum TINYINT NOT NULL DEFAULT 6, MaxTime TINYINT NOT NULL DEFAULT 2, MaxCount TINYINT NOT NULL DEFAULT 1 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE Reader ( ReaderAccount CHAR(15) PRIMARY KEY, ReaderType CHAR(4) NOT NULL, Available CHAR(1) NOT NULL DEFAULT 0, ReaderName CHAR(8) NOT NULL, ReaderSex CHAR(2) NOT NULL, ReaderSdept CHAR(10) NOT NULL, ReaderClass CHAR(10) NOT NULL, ReaderEmail CHAR(20) NOT NULL, ReaderMon SMALLINT NOT NULL DEFAULT 0, FOREIGN KEY (ReaderType) REFERENCES ReaderType(ReaderType) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;读者表的外键“读者账号”按文档应该同时参考账号信息表的主键所以还需要加一行FOREIGN KEY (ReaderAccount) REFERENCES UserMessage(UserName)。但要注意ReaderAccount和ReaderType同时都是外键时建表顺序必须是UserMessage和ReaderType先建好。Available字段默认“0”含义是“可用”这个默认值设对之后新读者注册进来不需要额外置位否则会漏掉初始化导致新用户借不了书。第三组是书籍相关表这里要做一次“四连建”书籍类型、某类书籍、阅览室、书架、书籍。CREATE TABLE BookType ( BookType CHAR(20) PRIMARY KEY, TypeName TEXT NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE BookInfo ( ISBN CHAR(20) PRIMARY KEY, BookName TEXT NOT NULL, Author CHAR(20) NOT NULL, BookKey CHAR(10) NOT NULL, BookEdit TEXT NOT NULL, PageNum SMALLINT NOT NULL, Price DECIMAL(10,2) NOT NULL, BookType CHAR(20) NOT NULL, PublishTime DATETIME NOT NULL, BookNum INT NOT NULL DEFAULT 0, BookIN INT NOT NULL DEFAULT 0, FOREIGN KEY (BookType) REFERENCES BookType(BookType) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE Room ( RoomNum CHAR(20) PRIMARY KEY, RoomName TEXT NOT NULL, RoomLoca TEXT NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE Shelf ( ShelfNum CHAR(20) PRIMARY KEY, RoomNum CHAR(20) NOT NULL, FOREIGN KEY (RoomNum) REFERENCES Room(RoomNum) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE Book ( BookNum CHAR(20) PRIMARY KEY, ISBN CHAR(20) NOT NULL, ShelfNum CHAR(20) NOT NULL, BookState CHAR(6) NOT NULL DEFAULT 在馆, BookDamage TINYINT NOT NULL DEFAULT 0, FOREIGN KEY (ISBN) REFERENCES BookInfo(ISBN), FOREIGN KEY (ShelfNum) REFERENCES Shelf(ShelfNum) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;对比文档我建书架表时去掉了“条形码”字段。原因是文档的书架表把条形码设成外键会形成“书架↔书籍”互相引用先建谁都报错。实际工程里书架的物理位置应该挂在书籍表上也就是Book.ShelfNum书架表只负责维护“哪个阅览室有哪些书架”否则一个书架只能放一本书完全不符合真实场景。后面第5章我会把这个坑单独拿出来讲。第四组是借阅和归还表主键和外键要同时考虑借阅表先建归还表后建。CREATE TABLE Borrow ( ReaderAccount CHAR(15) NOT NULL, BookNum CHAR(20) NOT NULL, BorTime DATETIME NOT NULL, BorCount CHAR(1) NOT NULL DEFAULT 0, PRIMARY KEY (ReaderAccount, BookNum, BorTime), FOREIGN KEY (ReaderAccount) REFERENCES Reader(ReaderAccount), FOREIGN KEY (BookNum) REFERENCES Book(BookNum) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE ReturnBook ( ReaderAccount CHAR(15) NOT NULL, BookNum CHAR(20) NOT NULL, BorTime DATETIME NOT NULL, ReturnTime DATETIME NOT NULL, BorCount CHAR(1) NOT NULL DEFAULT 0, PRIMARY KEY (ReaderAccount, BookNum, BorTime), FOREIGN KEY (ReaderAccount) REFERENCES Reader(ReaderAccount), FOREIGN KEY (BookNum) REFERENCES Book(BookNum) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意这里我把Borrow的主键设成了(ReaderAccount, BookNum, BorTime)而文档只写了ReaderAccount加BookNum。原因是同一读者在同一时间只能借同一本书一次但不同时间可以多次借同一本书如果不把借出日期加进主键第二次借这本书时会直接主键冲突。改造后的主键既保证唯一性又不会影响外键约束。BorCount用CHAR(1)存续借次数取值范围0到1这是文档规定的“最大续借次数2”含糊了实际上续借次数记录的是已续借的次数一般0或1就够。4. 数据流图拆解办证、借书、还书、缴费四条流程的存储变化数据流图是这份PDF里最容易被跳过的一部分因为比起E-R图它看起来更像“业务流程示意”。但考试和答辩都喜欢从数据流图切入问问题。数据流图的重点不是“谁做了什么动作”而是“数据从哪来、经过什么处理、存到哪个存储、流向哪个外部实体”。把这一点想清楚后面写后端接口都会顺很多。4.1 顶层图到0层图外部实体与处理过程怎么划分顶层数据流图只有两个外部实体读者和管理员再加上一个图书管理系统数据加工。读者向系统发出“办证”“借书”“还书”“缴费”请求管理员在系统里做验证操作。外部实体之间不能直接传数据流这是数据流图的铁律。到了0层图系统加工会被拆成四个核心处理办证处理、借书处理、还书处理、缴费处理。每个处理都要有输入数据流和输出数据流并且连到数据存储。文档里出现的“读者档案”“图书档案”“已借出档案”“还书档案”就是四个关键数据存储对应到关系模式里分别是读者表书籍表加某类书籍表读者借阅表读者归还表。很多同学画数据流图时只画箭头和圆圈不画存储评卷老师一眼就看出没有理解“数据流”的含义。数据流图里的箭头代表数据包在流动而不是控制信号。比如“验证信息失败”是从“验证信息”处理流回“读者检查办证”的数据流表示验证不通过的信息反馈。4.2 借书流程验证读者资格、更新书籍状态、写入借阅表借书流程在文档里体现为“读者验证资格→图书档案→已验证信息”这条链路。拆开来看实际借书涉及四次数据变化。第一步根据读者账号查读者表检查是否可用、读者类型是否有借书名额第二步根据条形码查书籍表看书籍状态是不是“在馆”第三步把书籍表的状态改成“不在馆”第四步向读者借阅表插入一条借出记录同时扣减某类书籍表里的在馆数量。这四步必须作为一个事务来处理否则会出现读者借书成功但书状态没改的脏数据。我在做课程设计的时候喜欢把这四步画成一张“存储变化表”数据存储名称、操作类型、涉及字段。这样答案不仅表达了数据流还能看出你是否理解表结构。比如对“图书档案”存储借书时执行的是“读改”读的是BookState改的也是BookState对“某类书籍”存储执行的是“读改”改的是BookIN字段对“已借出档案”存储执行的只有“插入”不修改。写清楚这些论文和答辩都很有说服力。4.3 还书与欠费流程归还表为什么必须保留“借出日期”还书流程比借书多一个“缴纳欠费”分支。读者把书给管理员后系统先在读者借阅表里找到借出记录读取借出日期和续借次数然后判断是否超过读者类型表的借书最大时间如果超期先计算欠费金额再走缴纳欠费处理更新读者表余额。最后把书的状态改回“在馆”更新某类书籍表在馆数量加1并向读者归还表插入一条归还记录。归还表必须保留“借出日期”而不是只存归还日期原因有两个。第一判断超期需要借出日期若归还表不复制这个字段还书时还得回借阅表反查一旦借阅表记录被清理历史超期信息就断了。第二归还表是审计轨迹借书日期和实际归还日期都有才能计算某本书的借阅周期也才能应对“这本书是不是逾期归还”的争议。文档里归还表完整保留了借出日期和续借次数这个设计不是冗余是有意为之。考试问“归还表为什么要有借出日期”时答案就是这里。4.4 数据流图落到后端代码PHP图书管理系统里的借书处理示例数据流图最终要变成代码。如果你用PHP写后端借书接口的核心逻辑和前面说的四步存储变化完全对应。我用PDO写了一个可运行的借书函数能体现事务、行锁和数据流映射。function borrowBook(PDO $pdo, string $readerAccount, string $bookNum): void { $pdo-beginTransaction(); try { // 1. 查读者是否可用 $stmt $pdo-prepare( SELECT ReaderAccount, Available, ReaderType FROM Reader WHERE ReaderAccount ? ); $stmt-execute([$readerAccount]); $reader $stmt-fetch(PDO::FETCH_ASSOC); if (!$reader || $reader[Available] ! 0) { throw new RuntimeException(读者不存在或不可用); } // 2. 锁定书籍记录防止并发借同一本 $stmt2 $pdo-prepare( SELECT BookNum, BookState, ISBN FROM Book WHERE BookNum ? FOR UPDATE ); $stmt2-execute([$bookNum]); $book $stmt2-fetch(PDO::FETCH_ASSOC); if (!$book || $book[BookState] ! 在馆) { throw new RuntimeException(书籍不在馆); } // 3. 更新书籍状态 $pdo-prepare( UPDATE Book SET BookState 不在馆 WHERE BookNum ? )-execute([$bookNum]); // 4. 插入借阅记录 $pdo-prepare( INSERT INTO Borrow (ReaderAccount, BookNum, BorTime, BorCount) VALUES (?, ?, NOW(), 0) )-execute([$readerAccount, $bookNum]); // 5. 某类书籍的在馆数量减一 $pdo-prepare( UPDATE BookInfo SET BookIN BookIN - 1 WHERE ISBN ? )-execute([$book[ISBN]]); $pdo-commit(); } catch (Throwable $e) { $pdo-rollBack(); throw $e; } }这里的关键点是FOR UPDATE行锁。在并发场景下两个管理员同时借同一本书如果不锁行两次查询都会看到“在馆”然后都执行插入书就被借出去两次。FOR UPDATE会锁住Book表里对应的那一行第二个事务必须等第一个提交后才能执行。PDO prepare和execute绑定参数是为了避免SQL注入课程设计里很多同学直接拼接字符串虽然能跑但现在连期末验收都会查这一项。事务包住五步操作后任何一步失败都会回滚保证数据流图里“图书档案更新”和“已借出档案插入”要么都成功要么都不生效。5. 避坑指南从这份PDF到能跑的系统五个高频翻车点PDF本身是完整的设计蓝本但照着它抄作业的时候至少有五个地方几乎人人都会踩。每条我都按“现象、原因、解决”来讲这也是我在帮人改课程设计时最常处理的几类问题。5.1 翻车点一分类与实例混为一谈丢掉了“某类书籍”这张表现象建表时嫌表面太多只做了一张“书籍”表把书名、作者、出版社、库存量、在馆数量全都塞进去条形码作为主键结果同一本书有5个副本就存了5遍完全相同的书名和作者。统计“馆藏总量”时压根不知道应该算哪一行只能COUNT(DISTINCT书名)数据一多就乱套。原因没有分清某类书籍和书籍两个实体。某类书籍是书目层的抽象概念一条记录代表一种书书籍是馆藏层的物理实体一条记录代表一册可借的书。前者存静态书目信息和汇总数量后者存动态状态和物理位置。解决严格按文档拆两张表。某类书籍表以ISBN为主键存书名、作者、主题、出版社、库存量、在馆数量书籍表以条形码为主键存ISBN、书架编号、书籍状态、损坏程度。任何涉及“这本书还有几本可借”的统计都去某类书籍表查涉及“这一册书在不在馆”的去书籍表查。5.2 翻车点二多对多关系只画一条线忘记拆中间表现象E-R图里读者和书籍之间只画一条带“借阅”标签的连线没有拆借阅表。到建表时发现读者表不知道该放哪个字段才能记录“借了哪几本书”于是有人把BookNum拼成字符串存在读者表里还有人建一张“借书表”只放读者账号和书名的TEXT字段完全没法做日期查询。原因把多对多联系当成了简单属性。借阅联系本身有属性借出日期、续借次数、实际归还日期。这些属性不能挂在读者或书籍任何一侧必须挂在联系对应的中间表上。解决遇到“读者和书籍通过借阅产生联系”先判定基数一个读者可以借多本书一本书可以被多个读者借过所以是多对多。多对多必须拆成两张中间表即文档里的读者借阅表和读者归还表。借阅表存当前在借归还表存历史记录主键建议用读者账号加条形码再加借出日期避免同一读者重复借同一本书时主键冲突。5.3 翻车点三外键参考错了表报表联查结果全是空现象查询借书列表时LEFT JOIN Reader和Book发现结果全是NULL或者结果重复增长。有些同学贴出来的报错是“Cannot add foreign key constraint”后面跟了一串乱码。检查表结构发现书籍表的ISBN外键指向了读者表的主键或者书架表外键指向了某类书籍表。原因照抄PDF关系模式时没有核对字段的归属。文档里有些表名有笔误比如某类书籍的关系模式写成ReaderType如果直接把这个错误的名字当表名外键自然建不上。另外书架表“条形码”外键指向书籍表书籍表“书架编号”外键又指向书架表循环依赖也会导致建表失败。解决建表前先画一张外键依赖图。依赖是单向的读者外键指向账号信息和读者类型书籍外键指向某类书籍和书架某类书籍外键指向书籍类型书架外键指向阅览室借阅表外键指向读者和书籍归还表外键指向读者和书籍。按照依赖顺序从被依赖的表开始创建。遇到循环依赖时按我第3章的处理方式去掉书架表的条形码外键把书架编号作为书籍表的外键即可不要让书架和书籍互相引用。5.4 翻车点四数据流图把数据存储漏掉画成了“流程图”现象交上去的数据流图看起来像泳道图只有“读者”“管理员”两个框中间一串箭头连到“验证”、“借出”、“归还”没有任何“数据存储”的图标。答辩论证时老师问“借书后书的状态存在哪里”答不上来。原因混淆了数据流图和流程图。流程图表达控制流箭头是“下一步做什么”数据流图表达数据流箭头是“有哪些数据在移动”并且必须经过数据存储。读者把书给管理员不是数据流“读者的借书请求”才是数据流。解决画数据流图时先列出外部实体、处理、数据存储三个维度。外部实体只放“读者”和“管理员”处理至少放“办证处理”“借书处理”“还书处理”“缴费处理”数据存储放“读者档案”“图书档案”“已借出档案”“还书档案”。每个处理必须至少有一条输入数据流和一条输出数据流连接到存储或外部实体漏掉任何一个箭头都要被追问。箭头命名用名词短语比如“读者档案信息”“已验证信息”不要用“验证一下”“返回结果”这种动词短语。5.5 翻车点五默认值和取值范围不写测试阶段就出现脏数据现象图书管理系统联调时发现“在馆数量”出现负数“书籍状态”里一半是“在馆”一半是“正常”“可借”“已借出”各种写法统计时COUNT结果对不上。读者类型表里借书上限填了“无限”后端解析时直接报类型错误。原因建表时没有把PDF字段定义表里的取值范围和默认值变成SQL约束。开发同学直接从接口接收前端传值前端下拉框如果漏配选项就会把脏数据写进数据库。没有默认值时“是否可用”字段可能是空的查出来是NULL而不是“0”。解决把文档里的默认值全部落到建表语句。书籍状态“在馆”设为默认值可用状态“0”设为默认值损坏程度和借阅上限用TINYINT并加CHECK约束。MySQL里可以这样写CHECK(BookDamage BETWEEN 0 AND 4)、CHECK(MaxBorNum BETWEEN 0 AND 10)。注意不要指望MySQL老版本自动执行严格Check开发阶段还要在应用层做参数校验双重保险。考试阶段不要求写应用层只要建表语句里带上DEFAULT和CHECK即可。6. 用这份PDF组织课程设计交付三件套自查与答辩话术6.1 三件套自查清单把E-R图、关系模式、数据流图对照检查半小时内能查完。表单向下面这样用检查项怎么查对应文档位置实体是否齐全数一下E-R图里的实体矩形是否覆盖读者、管理员、书籍、某类书籍、书架、阅览室、读者类型、书籍类型文档E-R图段落联系是否带基数每个菱形两侧是否标了1或N没有标的直接扣分联系描述外键是否闭合选一个联系看它对应的外键字段能不能连回主键表关系模式清单数据流是否指向存储数据流图里每个处理是否至少连到一个数据存储矩形数据流图段落默认值是否落库检查建表SQL里是否出现DEFAULT和CHECK字段定义表这套自查表是我现在做课程设计辅导时每次必用的。它不检查功能只检查设计完整性。设计完整性不过关代码写得再好也难拿高分。6.2 答辩现场怎么把这套设计讲清楚答辩讲十分钟我建议这样分配前两分钟讲E-R图重点讲某类书籍和书籍为什么拆开以及读者和书籍之间是M:N关系。中间四分钟讲关系模式和建表SQL现场投影出字段定义表强调“是否可用默认0”“损坏程度0到4”这些细节说明你是认真读过文档的。最后四分钟讲数据流图把办证、借书、还书、缴费四条链路各讲一句话配合借阅表和归还表的区别说清楚。有个小技巧答辩时把E-R图里的联系数量数出来并逐一说明它对应到哪张表的外键。这比背诵概念更能体现理解程度。比如你指着“拥有”联系说“读者表里有ReaderType外键指向读者类型表”老师就知道你是真做过设计而不是背的模板。我从那以后每次做课程设计都强制自己先走一遍“实体清单→联系判定→关系模式→字段定义→建表SQL”这五步。这份PDF的价值不在于让你直接复制答案而在于它示范了一个标准的信息系统设计过程先有图后有模式最后有字段。你把里面每一个字段默认值都问一次为什么就已经超过大部分交作业的人。希望帮到你。本文还有配套的精品资源点击获取
返回列表