多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

数据库课程设计图书馆管理系统:从ER图到SQL建表的完整方案

数据库课程设计图书馆管理系统:从ER图到SQL建表的完整方案 简介这是一份《数据库系统原理》课程设计文档——图书馆管理系统面向正在学习数据库原理、需要完成课程设计报告的高校学生。文档系统阐述了课程设计目的与意义、图书馆信息化项目背景并完整呈现可行性研究、需求分析与概要设计全过程。重点对图书馆管理系统的功能模块进行拆解包括基本信息维护、读者管理、图书管理、期刊管理、流通管理、统计分析、系统管理等其中读者类型设置、借书证挂失与恢复、图书征订与验收、期刊档案管理等子功能均有详细介绍可直接作为课程设计报告的撰写参考与答辩准备素材。资源包共含1个doc文档整体约117KB内容结构清晰完整。目前已有3406人浏览学习适合需要了解图书馆业务信息化需求、梳理数据库课程设计思路的同学下载研读。1. 数据库课程设计的图书馆管理系统这道题到底在考什么“数据库课程设计图书馆管理系统.doc”这个标题每年都会出现在不少学校的课程设计选题清单里看起来平平无奇但它背后覆盖的其实是一整套数据库核心技能需求分析、ER图、表结构设计、SQL建表、业务查询、事务处理、视图与存储过程一个不落。很多人拿到题目第一反应是“不就是写个图书增删改查嘛”结果答辩时被老师一句“你的ER图呢”问得当场愣住。这篇文章就从一个可落地的方案出发把从需求拆解到建表、从业务SQL到避坑排错的完整路径讲清楚。适合正在做数据库课设、或者想用最短时间把数据库核心能力串一遍的开发者参考。2. 需求与建模先把图书馆业务画成ER图再动手建表2.1 从借书还书反推业务流实体、关系与约束做数据库课程设计最容易犯的错是一上来就写CREATE TABLE写到中途发现漏了还书记录表再回头改结构改到崩溃。我的习惯是先花半个小时把业务流画出来哪怕只是在纸上画几个方框和连线。图书馆管理系统的核心业务其实只有三条线借书、还书、图书维护。借书的流程是读者查到想借的书确认这本书有库存管理员办理借出系统生成一条借阅记录图书库存减一。还书的流程是读者把书交回系统更新借阅记录的归还日期图书库存加一。图书维护则是管理员新增图书、修改图书信息、下架破损或丢失的书。这个流程一理清楚实体就浮出来了图书Book、读者Reader、管理员Admin以及借阅记录BorrowRecord。读者和图书之间是多对多关系一个读者可以借多本书一本书可以被多个读者在不同时间借阅。关系型数据库处理多对多关系必须拆成中间表这个中间表就是借阅记录表它同时承担了“谁在什么时间借了哪本书”的业务语义。在设计关系时要额外注意约束条件。比如一名读者最多只能借5本书这个约束如果只靠应用层判断并发场景下很容易被绕过数据库层必须能兜底。再比如库存为0的书不能被借出借阅记录一旦生成就不能再插入第二条相同的“在借”记录。这些约束在设计ER图时就要写清楚否则后面写存储过程时会反复返工。我的做法是先把这些规则列成一个清单每条对应一个数据库层面的实现手段外键约束、CHECK约束、触发器或者存储过程中的业务校验然后再进入表结构设计。2.2 表结构设计四张表的字段、主键与关系确定了实体之后下一步就是给每张表设计字段。先说图书表常见字段有图书ID、书名、作者、出版社、ISBN、分类、价格、库存量、状态。这里有一个关键选择主键用自增ID而不是ISBN。ISBN虽然看起来唯一但它不是严格的业务主键同一本书的精装版和平装版可能共用ISBN而且不同国家和地区的ISBN长度有差异拿它当主键会给自己挖坑。图书ID用INT UNSIGNED AUTO_INCREMENT省心且稳定其他字段再单独建唯一索引约束ISBN既满足业务又不影响主键结构。读者表的核心字段是读者ID、姓名、联系方式、注册日期、可借数量上限、状态。可借数量上限这个字段很多课设里会漏掉它是后面做并发控制和超借判断的基础建议一开始就设计进去。管理员表则包括管理员ID、账号、密码、姓名、角色。密码字段的存储建议用哈希值而不是明文虽然课程设计对安全性的要求不高但这是一个很自然的加分细节答辩时主动提出来效果很好。借阅记录表是整个系统的核心它的字段包括记录ID、图书ID、读者ID、借出日期、应还日期、实际归还日期、状态。这里要特别注意应还日期和实际归还日期是两回事。应还日期在借出时根据借期规则算出来通常是借出日期加30天实际归还日期在还书时写入。只有同时保留这两个日期才能准确计算逾期天数和逾期费用。不少同学只存一个归还日期结果逾期统计无从谈起这是设计阶段的典型疏漏。2.3 规范化设计为什么借阅记录表不能冗余读者姓名和书名课程设计老师特别喜欢问一个问题“你的表满足第几范式”如果借阅记录表里同时存了读者姓名和书名那这个设计就不满足第三范式因为读者姓名依赖读者ID书名依赖图书ID而借阅记录表的主键是记录ID非主属性之间存在传递依赖。正确做法是借阅记录表只存读者ID和图书ID需要显示姓名和书名时通过JOIN去关联查询。我见过不少同学为了查询方便在借阅记录表里冗余了书名和读者姓名省去了JOIN的麻烦结果被老师追问“如果读者改了姓名历史借阅记录怎么办”。所以做课程设计时老老实实按三范式来设计查询多写JOIN性能在数据量几百条的场景下完全不是问题。说实话课设数据量撑死几千行根本不需要考虑反规范化规范化带来的数据一致性收益远大于那点性能损耗。如果后续想做扩展比如记录借书时的快照信息可以单独建表存历史快照而不是把冗余字段塞进主表。2.4 字段类型与常见选择int、varchar、datetime怎么定不翻车字段类型的选择是答辩时的常客话题。图书表的ID用INT UNSIGNED AUTO_INCREMENT别用BIGINT也别用VARCHARINT占4字节最大支持21亿多图书馆藏量不可能超这个量级。书名、作者这类文本字段用VARCHAR长度按需给书名给200作者给100不要一律255这既是习惯也是规范。价格用DECIMAL(10,2)而不是FLOATFLOAT有精度问题0.1加0.2不等于0.3这种事放到金额上是不能接受的。日期字段用DATE还是DATETIME取决于业务是否需要时分秒。借书和还书只需要日期用DATE就够。但有些学校要求记录具体操作时间这时候用DATETIME默认值设为CURRENT_TIMESTAMP让数据库自动写入时间。状态字段是另一个容易忽略的点。图书状态、读者状态、借阅记录状态我统一用TINYINT0表示正常或已归还1表示已借出或禁用2表示逾期等。用数字而不是字符串的好处是存储小、比较快、语义稳定坏处是代码里要写好注释。课设数据量小VARCHAR状态也能跑但从习惯上我更推荐TINYINT加状态码注释。字段场景推荐类型不推荐类型原因主键INT UNSIGNED AUTO_INCREMENTVARCHAR自增稳定无业务耦合价格/金额DECIMAL(10,2)FLOAT/DOUBLE避免浮点精度误差日期DATE/DATETIMEVARCHAR支持日期函数与索引状态TINYINTVARCHAR存储小比较快枚举可控联系电话VARCHAR(20)INTINT丢前导0位数受限3. 从SQL建表到数据初始化图书馆管理系统的最小可运行版本3.1 建库建表SQL主键、外键、索引与默认值一次到位设计讲完就到了动手阶段。我用MySQL 8.0做演示这也是目前课程设计用得最多的版本。如果学校要求用SQL Server或者Oracle表结构逻辑完全一致只有部分数据类型的语法需要改。先建库这一步一定要指定字符集否则后面中文乱码的坑一个接一个。CREATE DATABASE IF NOT EXISTS library_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;utf8mb4是MySQL的完整UTF-8实现能存emoji和生僻字utf8mb4_unicode_ci是大小写不敏感的比较规则。MySQL 5.7及以上的版本都支持utf8mb4放心用。如果已经建好了库但忘了指定字符集可以通过ALTER DATABASE补救但建表时的默认字符集会跟随库级设置改起来容易有连锁问题所以第一步就要做对。然后建图书表CREATE TABLE IF NOT EXISTS book ( book_id INT UNSIGNED AUTO_INCREMENT COMMENT 图书ID, title VARCHAR(200) NOT NULL COMMENT 书名, author VARCHAR(100) NOT NULL COMMENT 作者, publisher VARCHAR(150) DEFAULT NULL COMMENT 出版社, isbn VARCHAR(20) DEFAULT NULL COMMENT ISBN号, category VARCHAR(50) DEFAULT NULL COMMENT 分类, price DECIMAL(10,2) DEFAULT 0.00 COMMENT 价格, stock INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 库存数量, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0正常1下架, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (book_id), UNIQUE KEY uk_isbn (isbn), KEY idx_title (title), KEY idx_category (category) ) ENGINEInnoDB COMMENT图书表;字段注释COMMENT一定要写课程设计评分表里通常有一项叫“表结构规范性”注释和命名规范都在考核范围。status字段控制图书是否可借下架的书不进入借阅流程。stock是总库存每借出一本减一。isbn建了唯一索引但主键仍然是自增ID这样既不违反业务唯一性又避免ISBN变化带来的主键维护问题。title和category建普通索引对应后面“按书名模糊查询”和“按分类统计”两个高频操作。price用DECIMAL已经说过不再赘述。读者表和借阅记录表CREATE TABLE IF NOT EXISTS reader ( reader_id INT UNSIGNED AUTO_INCREMENT COMMENT 读者ID, name VARCHAR(100) NOT NULL COMMENT 姓名, phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, max_borrow TINYINT UNSIGNED NOT NULL DEFAULT 5 COMMENT 最大可借数量, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0正常1禁用, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, PRIMARY KEY (reader_id), KEY idx_name (name) ) ENGINEInnoDB COMMENT读者表; CREATE TABLE IF NOT EXISTS borrow_record ( record_id INT UNSIGNED AUTO_INCREMENT COMMENT 记录ID, book_id INT UNSIGNED NOT NULL COMMENT 图书ID, reader_id INT UNSIGNED NOT NULL COMMENT 读者ID, borrow_date DATE NOT NULL COMMENT 借出日期, due_date DATE NOT NULL COMMENT 应还日期, return_date DATE DEFAULT NULL COMMENT 实际归还日期, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0借出中1已归还2逾期, PRIMARY KEY (record_id), KEY idx_book_id (book_id), KEY idx_reader_id (reader_id), KEY idx_due_date (due_date), CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book (book_id), CONSTRAINT fk_borrow_reader FOREIGN KEY (reader_id) REFERENCES reader (reader_id) ) ENGINEInnoDB COMMENT借阅记录表;借阅记录表建了三个普通索引分别对应“按图书查借阅历史”“按读者查当前借阅”“按应还日期查逾期清单”。外键保留的是物理外键在课设里是加分项能保证引用完整性。但要提醒一句物理外键在后期删除和更新操作上会比较麻烦比如删除一本有借阅记录的图书会直接报错。这个坑第5章专门讲我这里先按下不表。提示如果学校提供的数据库账号没有CREATE DATABASE权限可以跳过建库语句直接在你被授权的库里建表但表名最好加前缀如stu_library_book避免和其他人冲突。3.2 初始化数据种子数据怎么写状态字段的值从哪来表建好后得写入一些演示数据才能跑业务流程。初始化数据要覆盖正常、异常、边界三种情况。图书表里要有库存为0的书、状态为下架的书读者表里要有被禁用的读者借阅记录表里要有已归还的、借出中的、逾期的记录。这样后面演示查询和统计时每个SQL都能取出有效结果。INSERT INTO book (title, author, publisher, isbn, category, price, stock, status) VALUES (数据库系统概论, 某作者甲, 某出版社, 978-7-0000-0001, 计算机, 59.00, 5, 0), (计算机网络, 某作者乙, 某出版社, 978-7-0000-0002, 计算机, 49.00, 3, 0), (深度学习基础, 某作者丙, 某出版社, 978-7-0000-0003, 人工智能, 89.00, 0, 0), (三体, 某作者丁, 某出版社, 978-7-0000-0004, 文学, 39.00, 10, 1);这里故意让第三本书库存为0、第四本书状态为1就是为了后面的“库存不足不能借”和“下架图书不可借”这两个业务判断有真实数据可验证。初始化数据不是随便填几条最好对着业务场景一条条设计后面写SQL时你会发现每条数据都有它的用途。读者和借阅记录表的初始化同理可以插一个max_borrow为2的读者用来测试超借拦截。3.3 验证环境用Python脚本连上数据库跑通一个查询有些人建完表、插完数据就算完事等到写后端接口时才发现驱动没装、连接报错问题堆到答辩前夜才集中爆发。我习惯在表结构完成之后立刻写一个最小连接脚本确认数据库驱动、账号权限、网络连通性都没问题。用Python加pymysql做演示import pymysql conn pymysql.connect( host127.0.0.1, port3306, userroot, passwordyour_password, databaselibrary_db, charsetutf8mb4 ) cursor conn.cursor() cursor.execute(SELECT book_id, title, stock FROM book) for row in cursor.fetchall(): print(row) cursor.close() conn.close()这个脚本没有任何业务逻辑只做一件事验证从应用层到数据库的整条链路是通的。host、port、user、password按实际环境改注意charset必须写utf8mb4否则即使数据库建库时是utf8mb4连接层也可能因为字符集不一致返回乱码或者报错。如果你的项目用JDBC连MySQL连接串里也需要加上characterEncodingUTF-8和useSSLfalse之类的参数。我用这个脚本做连通性自测确认无误后再开始写业务代码能省掉大量后续排查时间。4. 业务SQL与视图存储过程课程设计的评分点都在这里4.1 借书与还书的事务SQL状态流转和并发控制怎么落借书操作涉及两张表的变更book表的stock减一borrow_record表插入一条记录。这两步必须在一个事务里执行否则会出现库存扣了但借阅记录没生成的数据不一致。MySQL的写法是START TRANSACTION; UPDATE book SET stock stock - 1 WHERE book_id 101 AND stock 0 AND status 0; INSERT INTO borrow_record (book_id, reader_id, borrow_date, due_date, status) VALUES (101, 201, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 30 DAY), 0); COMMIT;这段SQL的核心在UPDATE的WHERE条件里加了stock 0。如果库存为0UPDATE的受影响行数是0后续插入借阅记录就没有意义。所以在真实代码里会先执行UPDATE再判断受影响行数只有行数等于1时才插入和提交。这个模式叫条件更新它比先SELECT再UPDATE更安全因为SELECT和UPDATE之间存在时间窗口两个并发请求可能同时读到库存为1然后同时通过判断最终把库存扣成负数。借出日期用CURDATE()取数据库服务器当天日期应还日期用DATE_ADD(CURDATE(), INTERVAL 30 DAY)计算30天是默认借期。这个函数在MySQL和MariaDB里都支持但Oracle的写法是ADD_MONTHSSQL Server是DATEADD换数据库时要对应调整这也是答辩时容易被追问的跨数据库差异点。还书操作是借书的逆向过程START TRANSACTION; UPDATE book SET stock stock 1 WHERE book_id 101; UPDATE borrow_record SET return_date CURDATE(), status IF(CURDATE() due_date, 2, 1) WHERE record_id 5001 AND return_date IS NULL; COMMIT;还书先加库存再更新借阅记录。status用IF判断是否逾期当前日期大于应还日期则置为2逾期否则置为1已归还。这里有个细节更新借阅记录时WHERE要带return_date IS NULL防止同一条记录被重复归还。事务的隔离级别默认是REPEATABLE READ在课设这个量级完全够用不用调整。如果老师问“事务的ACID怎么体现”你就指着这两段SQL讲原子性和一致性再补充说InnoDB通过redo log保证持久性通过锁和MVCC保证隔离性。4.2 分组统计与连接查询热门图书、逾期未还、分类统计每个课程设计都少不了一个统计报表模块而统计模块主要靠GROUP BY和JOIN撑起来。比如查询当前借出次数最多的TOP5图书SELECT b.title, COUNT(br.record_id) AS borrow_count FROM book b JOIN borrow_record br ON b.book_id br.book_id GROUP BY b.book_id, b.title ORDER BY borrow_count DESC LIMIT 5;这里GROUP BY了b.book_id和b.title这是MySQL的一个经典要求SELECT中出现的非聚合列必须出现在GROUP BY中否则在ONLY_FULL_GROUP_BY模式下会直接报错。如果只GROUP BY b.book_id有些版本会因为功能依赖关系而允许但为了保险把title也带上最稳妥。逾期未还列表是课程设计里必做的查询SELECT r.name AS reader_name, b.title AS book_name, br.borrow_date, br.due_date, DATEDIFF(CURDATE(), br.due_date) AS overdue_days FROM borrow_record br JOIN reader r ON br.reader_id r.reader_id JOIN book b ON br.book_id b.book_id WHERE br.status 0 AND br.due_date CURDATE();注意WHERE条件是due_date CURDATE()而不是DATEDIFF(CURDATE(), due_date) 0因为对索引列套函数会让索引失效导致全表扫描。课程设计数据量小全表扫描也能跑出来但工作后这个习惯会被面试官重点考察“为什么不在索引列上使用函数”记住了过滤条件直接比较原始列让索引生效。分类统计是GROUP BY加COUNT的模板SELECT category, COUNT(*) AS total_books, SUM(stock) AS total_stock FROM book WHERE status 0 GROUP BY category;4.3 视图与存储过程哪些逻辑应该封装哪些没必要视图适合封装频繁复用的查询SQL存储过程适合封装包含事务操作的多步逻辑。视图的经典用途是定义“当前借出中”的集合后续所有需要“当前在借”的查询都能直接引用。CREATE OR REPLACE VIEW v_current_borrow AS SELECT br.record_id, br.book_id, b.title, r.name AS reader_name, br.borrow_date, br.due_date FROM borrow_record br JOIN book b ON br.book_id b.book_id JOIN reader r ON br.reader_id r.reader_id WHERE br.status 0;存储过程的典型场景是把借书整套事务封装成一个可复用过程不管Java后端还是Python脚本都能通过CALL borrow_book(101, 201)完成借书DELIMITER $$ CREATE PROCEDURE borrow_book(IN p_book_id INT, IN p_reader_id INT) BEGIN DECLARE v_stock INT; DECLARE v_max INT; DECLARE v_borrowing INT; SELECT stock INTO v_stock FROM book WHERE book_id p_book_id FOR UPDATE; IF v_stock 0 THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 库存不足; END IF; SELECT COUNT(*) INTO v_borrowing FROM borrow_record WHERE reader_id p_reader_id AND status 0; SELECT max_borrow INTO v_max FROM reader WHERE reader_id p_reader_id; IF v_borrowing v_max THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 超出可借数量; END IF; INSERT INTO borrow_record (book_id, reader_id, borrow_date, due_date, status) VALUES (p_book_id, p_reader_id, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 30 DAY), 0); UPDATE book SET stock stock - 1 WHERE book_id p_book_id; END$$ DELIMITER ;这段存储过程演示了两个容易被忽视的检查库存检查和读者可借数量检查。SELECT ... FOR UPDATE对book表的对应行加锁避免并发时库存被多借。读者当前借阅数通过COUNT(*)加条件status0计算。SIGNAL语句在MySQL 5.6及以上版本可用用于主动抛出业务错误Java后端可以捕获到SQLException。写完存储过程一定不要忘了DELIMITER的切换。DELIMITER是MySQL命令行客户端的指令告诉客户端“接下来用$$当语句结束符”因为存储过程内部的分号会被MySQL误认为是语句终止符。Navicat等图形工具通常不需要手动写DELIMITER工具会帮忙处理但命令行操作绕不开这是答辩现场很常见的卡壳点。5. 避坑指南图书馆管理系统从建表到答辩的常见问题排查5.1 中文乱码一个连接参数引发的血案现象插入的中文数据在命令行查询时显示正常但Java前端页面显示全是问号或者反过来命令行乱码但页面正常。原因乱码问题有三个层面数据库字符集、连接字符集、前端页面字符集。最容易被忽视的是连接层的charset参数。MySQL 8.0默认字符集是utf8mb4但JDBC连接串里如果没指定characterEncodingUTF-8驱动可能使用服务器默认字符集两边对不上就乱码。Python的pymysql如果connect参数里没写charsetutf8mb4同样会乱码。解决三层各检查一遍缺一不可。建库时指定utf8mb4连接参数里指定utf8mb4前端页面声明UTF-8。检查下面这个变量逐个确认当前连接字符集SHOW VARIABLES LIKE character_set%;5.2 外键约束引发的连锁反应删不掉的书、改不了的ID现象想删除一本没有任何在借记录但有历史借阅记录的图书直接报错Cannot delete or update a parent row: a foreign key constraint fails。原因borrow_record表通过外键引用了book表默认的ON DELETE规则是RESTRICT存在子表引用时禁止删除父表记录。对于图书馆业务来说一本书只要有借阅历史就没法物理删除。解决我的建议是业务上不物理删除图书删除就是修改status为1下架历史数据完整逻辑也站得住。如果老师一定要看DELETE语句可以在事务里先逻辑删除相关借阅记录或把外键改为ON DELETE SET NULL但后者会让借阅历史失去图书ID的追溯能力我不推荐。答辩时主动说出“物理删除会破坏历史借阅数据所以系统采用逻辑删除”这本身就是数据库设计素养的体现。5.3 逾期计算差一天日期边界的经典误差现象应还日期是2024-06-30读者在2024-06-30当天还书系统却提示逾期1天。原因判断条件写成了DATEDIFF(CURDATE(), due_date) 1。DATEDIFF在当天和应还日期的差值是0但有些代码写了大于等于1的判断导致当天还书被误判逾期。更隐蔽的是如果还书日的时刻晚于借出日对应时刻只比较日期也可能差一天。解决逾期判断用CURDATE() due_date而不是日期差大于阈值。查询逾期清单时也用due_date CURDATE()。如果你存了时分秒建议用DATE(due_date)转成日期再比较避免时间部分干扰判断。5.4 并发超借SELECT判断库存的经典竞态现象演示时开了两个浏览器窗口同时借同一本只剩1本库存的书两个请求都显示成功数据库库存变成了-1。原因两个请求几乎同时执行SELECT stock FROM book WHERE book_id101都读到1判断大于0然后各自执行UPDATE stockstock-1最终库存变成-1。SELECT和UPDATE之间没有锁保护形成竞态条件。解决用单条UPDATE完成扣减和校验把库存判断放进WHERE条件再根据受影响行数决定后续动作。UPDATE book SET stock stock - 1 WHERE book_id 101 AND stock 0 AND status 0;这条语句执行后如果受影响行数是0说明库存不足或下架直接返回“借书失败”。这条UPDATE本身是原子的数据库行锁会保证并发安全。这个写法是数据库并发控制的经典答案面试问到“超卖问题”时也是同一套思路。5.5 答辩现场系统起不来备份与恢复的保命操作现象答辩前夜电脑上的MySQL服务崩溃数据文件损坏第二天要演示的系统直接黑屏。原因开发周期长表结构改了又改数据插了又删整个过程没有任何备份出了问题只能干瞪眼。解决从第一天开始每完成一个阶段就导出一份备份。mysqldump把整个库的结构和数据导成SQL文件mysqldump -u root -p library_db library_backup_$(date %Y%m%d).sql恢复时mysql -u root -p library_db library_backup_20240601.sql我习惯在答辩前一周每天导出一次文件名带日期改数据前先备份改崩了还能回滚到前一天。另外交课程设计时老师通常要求附数据库初始化脚本这个mysqldump导出的SQL文件正好原样上交一份文件解决备份和交付两件事。5.6 建表后返工字段长度与类型的连锁修改现象ISBN字段设计成VARCHAR(13)插入978开头的17位ISBN号时直接被截断备份还原时也报数据不一致。原因ISBN-10确实是10位但从2007年开始国际标准书号升级为13位ISBN-13。设计表结构时只考虑了旧版本没给未来留余地。解决ISBN字段用VARCHAR(20)预留空间。同类问题还有手机号存INT导致前导0丢失以及没有考虑座机带区号的情况。建表前把每个字段的主流取值和未来扩展都想一下不要拍脑袋定长度。字段长度修改本身不难但如果有外键关联和索引ALTER TABLE的代价会放大最好在最初设计时就多留一点余量。6. 把图书馆管理系统做成简历加分项触发器、优化与答辩演示6.1 用触发器实现业务扩展借阅日志自动化外键和存储过程拿下了基础分但课程设计想冲高分还得展示数据库的“主动能力”。触发器是一个性价比很高的加分项。比如在borrow_record表上创建一个AFTER INSERT触发器自动往operation_log表写一条操作日志记录谁在什么时间借了哪本书。这样借书业务不需要在应用层额外写日志代码数据库层面自动完成。CREATE TABLE operation_log ( log_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, op_type VARCHAR(20) NOT NULL, detail VARCHAR(500) NOT NULL, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; DELIMITER $$ CREATE TRIGGER trg_borrow_log AFTER INSERT ON borrow_record FOR EACH ROW BEGIN INSERT INTO operation_log (op_type, detail) VALUES (BORROW, CONCAT(reader_id, NEW.reader_id, , book_id, NEW.book_id)); END$$ DELIMITER ;触发器要克制使用生产环境里触发器过多会带来排障困难但在课设量级它既展示了你对数据库主动机制的理解又不会造成实际问题属于考试技巧范畴。演示时顺手查一下operation_log表证明日志自动生成了很加分。6.2 EXPLAIN一句SQL看清索引是否生效如果演示时某条查询明显变慢可以在SQL前面加EXPLAIN关键字查看执行计划。重点关注type列出现const、eq_ref或ref说明索引生效出现ALL说明是全表扫描。课程设计的数据量不该出现全表扫描碰到ALL就加索引。例如常用的where条件里有reader_id确保reader_id上有索引有due_date确保due_date上有索引。这个习惯带到工作后也有直接价值简历上写“具备SQL调优基础能力”时能言之有物。6.3 演示脚本与答辩文档按顺序讲评分不会差我的习惯是给课设配一份演示脚本先展示初始化数据再演示借书打印库存变化和借阅记录再演示还书打印逾期判断最后跑统计查询和视图。每个SQL都设计成能展示一个知识点而不是机械地“点点点”。文档方面ER图、数据字典、关系模式说明、每个SQL的设计理由四件套不能少。很多同学只贴代码不写“为什么这样设计”答辩时被老师追问就接不上话。提前把字段类型选择、索引设计、事务边界这些理由写清楚自己也不容易忘。我在做这类课设时最深的感受是数据库课程设计的评分点从来不在功能多而在每一步设计都能说出依据。从字段类型为什么选INT、为什么建这个索引到为什么要用事务和存储过程每一个决定背后都要有一个能讲清楚的故事。希望你做完这个系统之后不仅能拿下一个好分数还能在面试官问“你的项目数据库是怎么设计的”时回答得从容、具体、有层次。希望帮到你。本文还有配套的精品资源点击获取
返回列表