简介:这是北京信息科技大学《数据库系统及应用实践》课程设计报告,主题为校园二手交易系统,适合数据库初学者、高校学生以及正在准备课程设计的读者参考。报告以独立实践项目为主线,完整记录了从需求分析、概念数据模型(E-R)、逻辑模型到物理模型的设计过程,并在物理模型阶段补充了约束、视图、触发器、存储过程、安全管理、备份恢复与事务设计等内容,同时给出基于SQL Server和PowerDesigner的建库脚本与实施规划,还包含需求说明书、附件样例和个人总结,能够帮助读者系统理解数据库设计的完整流程。资源包内为1个doc文档,大小约1.72MB,正文与附件结构清晰,覆盖面较广。目前已有1572人学习,适合需要参考课程设计报告,或想提升数据库建模与开发能力的同学借鉴。
1. 数据库课程设计到底在做什么:从记账本到进销存的最小闭环
看到「数据库课程设计(5).doc」这个文件名,我第一反应是:这份文档大概率不是第一次写了,前面的 4 版要么跑不通,要么被老师打回。数据库课程设计不是写作文,也不是把教材里的 SQL 抄一遍,而是用一套完整的数据库方案去解决一个真实业务问题。图书管理、学生选课、超市进销存,这些是老生常谈的选题,但真正能一口气走完的需求文档、ER 图、建表脚本、增删改查、并发控制、备份恢复,加一份能讲清楚为什么这样设计的答辩稿,才算落地。这篇笔记就是照着这个标题,把从零到一的一条完整路径走一遍,新手可以顺着做,熟手可以直接跳到第 5 章的坑,以及第 6 章怎么验证设计到底靠不靠谱。
2. 选型与建模:先说清业务,再决定 MySQL 还是 SQLite
很多课设一上来就打开 Navicat 新建数据库,这是翻车的第一步。数据库课程设计的第一步不是选数据库软件,而是把业务描述变成数据字典。我一般会先问三个问题:系统里有哪些角色?每个角色要记录什么关键信息?哪些信息之间能通过编号或时间关联起来?这三个问题的答案就是实体、属性和联系。
做课设的人最容易犯的毛病是拿到题目就画 ER 图,画到一半发现实体之间根本对不上。所以我的习惯是:先用表格把数据字典列清楚,再动画图工具。数据字典是后面所有工作的地基,它比 ER 图更接近建表逻辑。
2.1 用数据字典把需求变成实体:别一上来就画 ER 图
以超市进销存为例,原始需求通常是「记录商品的进货和销售,能查库存,能统计某段时间卖了多少钱」。第一版需求很粗,直接写「商品表里再加进货价、销售价、供应商、进货时间、销售时间」是很多人的本能反应。但这样做会在更新数据时覆盖掉历史记录,因为一张商品对应多条进货记录,字段塞不下就只剩「最后一次」的数据了。
正确做法是先拆实体。商品是一个独立实体,供应商是一个独立实体,进货单是记录「一次进货行为」的实体,而进货明细负责记录「这次进货里具体进了哪些商品、各多少、什么价」。同样,销售单和销售明细也要拆开。把实体拆出来之后,再给每个实体列属性,就形成了数据字典。
| 实体 | 属性 | 类型建议 | 约束说明 |
|---|---|---|---|
| 商品 | 商品编号、名称、进价、售价、当前库存 | INT、VARCHAR、DECIMAL、INT | 主键、非空、唯一名称 |
| 供应商 | 供应商编号、名称、电话、地址 | INT、VARCHAR | 主键、非空 |
| 进货单 | 进货单号、供应商编号、进货日期 | INT、INT、DATE | 主键、外键、非空 |
| 进货明细 | 明细编号、进货单号、商品编号、数量、进价 | INT、INT、INT、INT、DECIMAL | 主键、外键、非空 |
这张表就是数据字典的核心形态。注意「类型建议」和「约束说明」要写在文档里,因为后面建表要直接对着写。很多课程设计报告只截一个 ER 图,没有数据字典,答辩时老师问「为什么这个字段不能为空」就答不上来。数据字典的价值就是逼你把每个字段的边界条件想清楚。
我见过最典型的血泪教训是:有人把所有字段堆在一张表里,把进货明细和销售明细当作商品表的两个文本字段,用逗号拼商品编号。查询时不得不写字符串拆分函数,性能一塌糊涂,还被老师批「不符合第一范式」。所以,宁可多拆一张表,也不要把数组塞进一个字段。
2.2 关系模式与 ER 图的落地写法:从 1:1、1:N、M:N 到三张表
数据字典有了,再画 ER 图就不容易跑偏。ER 图里最需要动脑的是联系的类型。供应商和商品之间是「一个供应商供多种商品,一种商品也可以有多个供应商」,这是 M:N。进货单和商品之间同样是 M:N,因为一张进货单有多个商品,一个商品会被多次进货。M:N 不能直接落成两张表,必须拆出一张中间表,也就是进货明细。
关系模式的写法如下:
- 商品(商品编号,名称,进价,售价,当前库存)
- 供应商(供应商编号,名称,电话)
- 进货单(进货单号,供应商编号,进货日期)
- 进货明细(明细编号,进货单号,商品编号,数量,进价)
其中下划线表示主键,斜体表示外键。进货单与供应商是 N:1,进货明细与进货单是 N:1,进货明细与商品是 N:1。这里的关键是:中间表的主键可以单独设置自增明细编号,也可以用「进货单号 + 商品编号」做复合主键。我建议课设里加一个单独的自增主键,不要用复合主键,因为后续做更新和删除时,单字段主键写起来更简单。
还有一类联系容易被忽略:1:1。比如「员工表」和「员工账户表」,一个员工只有一个登录账户,这时候可以把账户字段直接放在员工表里,也可以拆出来一对一关联。课设中大多数 1:1 可以直接合并,减少 JOIN 次数。真正需要单独拆表的,是那种「一部分员工有特殊信息、其他员工没有」的情况,但不是课设的考察重点。
关系模式写完后,建议在文档里用一句话解释每个外键的来源。例如「进货明细.商品编号 引用 商品.商品编号,保证明细中的商品必须在商品库中存在」。这句话能帮助答辩老师在五分钟内看明白你的设计思路。很多课设报告只放 ER 图不放关系模式,这是不够的,因为 ER 图是图,关系模式才是「能直接生成建表语句的中间产物」。
3. 用 DDL 建库建表:字段类型、主外键与三范式之间的取舍
建模完成之后,下一步就是把关系模式翻译成 DDL。这一步看起来只是体力活,但实际上有三个容易翻车的点:字段类型选错、字符集不统一、外键约束写漏。如果用错,后面 CRUD 阶段会不停报错。
3.1 建库建表 SQL 模板:从 MySQL 到 SQLite 的差异
以 MySQL 8.0 为例,先建库再建表。注意字符集必须显式指定,不要依赖你的本机默认配置。
-- 建库:MySQL 8.0 默认 utf8mb4,但显式写出来最保险 CREATE DATABASE IF NOT EXISTS supermarket DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE supermarket; -- 建商品表 CREATE TABLE product ( product_id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '商品编号', product_name VARCHAR(100) NOT NULL COMMENT '商品名称', purchase_price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '进价', sale_price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '售价', stock INT NOT NULL DEFAULT 0 COMMENT '当前库存', PRIMARY KEY (product_id), UNIQUE KEY uk_product_name (product_name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';这段建表 SQL 里每个选择都有理由。INT UNSIGNED 表示无符号整数,防止商品编号出现负数;AUTO_INCREMENT 让主键自增,省去手动编号。VARCHAR(100) 给名称留出足够空间,不会因为写漏长度导致中文插入失败。DECIMAL(10,2) 是定点数,如果这里用 FLOAT,累计金额时会出现 0.1 + 0.2 = 0.30000000000000004 这种精度错误。UNIQUE KEY 放在商品名称上,是为了避免同一商品被重复录入两次。ENGINE=InnoDB 是关键,它支持事务和外键,而 MyISAM 不支持,课程设计如果涉及并发更新,必须用 InnoDB。
-- 建供应商表 CREATE TABLE supplier ( supplier_id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '供应商编号', supplier_name VARCHAR(100) NOT NULL COMMENT '供应商名称', phone VARCHAR(20) NOT NULL COMMENT '联系电话', PRIMARY KEY (supplier_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商表'; -- 建进货单表 CREATE TABLE purchase_sheet ( sheet_id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '进货单号', supplier_id INT UNSIGNED NOT NULL COMMENT '供应商编号', purchase_date DATE NOT NULL COMMENT '进货日期', PRIMARY KEY (sheet_id), KEY idx_supplier_id (supplier_id), CONSTRAINT fk_purchase_supplier FOREIGN KEY (supplier_id) REFERENCES supplier (supplier_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='进货单表';外键约束直接写在 CREATE TABLE 里,用 CONSTRAINT 起一个名字,这样以后想删外键时知道删谁。外键字段上要建一个普通索引,否则 MySQL 会自动建,但名字乱糟糟,不方便维护。进货日期用 DATE 类型,不要用 VARCHAR。DATE 可以直接排序、范围比较,而字符串格式的日期会按字典序比较,导致 2024-01-02 排到 2023-11-30 前面。
如果你用的是 SQLite,情况会不一样。SQLite 没有 AUTO_INCREMENT,要写 INTEGER PRIMARY KEY AUTOINCREMENT;DECIMAL 会被当成 NUMERIC,精度没有 MySQL 那么严格;最关键的是 SQLite 不支持 ALTER TABLE 修改列名,也不支持完整的 FOREIGN KEY 行为,很多时候外键约束只在开启外键开关时生效。所以课设里我建议优先用 MySQL,除非老师明确指定 SQLite。SQLite 更适合做嵌入式单机工具,不适合演示并发锁和复杂事务。
3.2 索引与约束怎么加:哪些字段值得建索引,哪些是自找麻烦
课设的数据量通常只有几百到几千条,索引的加速效果感知不强,但答辩时老师一定会问「你哪些字段建了索引?为什么这么建?」。所以这不是性能问题,而是设计能力问题。
-- 在进货明细表上给外键建索引,避免联表查询时全表扫描 CREATE TABLE purchase_detail ( detail_id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细编号', sheet_id INT UNSIGNED NOT NULL COMMENT '进货单号', product_id INT UNSIGNED NOT NULL COMMENT '商品编号', quantity INT NOT NULL DEFAULT 1 COMMENT '数量', price DECIMAL(10,2) NOT NULL COMMENT '实际进价', PRIMARY KEY (detail_id), KEY idx_sheet_id (sheet_id), KEY idx_product_id (product_id), CONSTRAINT fk_detail_sheet FOREIGN KEY (sheet_id) REFERENCES purchase_sheet (sheet_id), CONSTRAINT fk_detail_product FOREIGN KEY (product_id) REFERENCES product (product_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='进货明细表';外键字段建索引的理由是为了让 JOIN 时能快速找到对应行。如果不建,MySQL 会隐式创建一个索引,但如果你用 SHOW INDEX 查看,会发现索引名不是你控制的。课设演示时,你可以用 EXPLAIN SELECT 来证明某个查询走了索引。
但要注意,不是每个字段都值得加索引。比如性别、状态这种取值很少的字段,加索引的区分度很低,反而增加写入成本。库存字段如果经常被 UPDATE,索引也会增加更新代价。在课设里,一般只对主键、外键、以及经常出现在 WHERE 条件里的字段建索引。比如按商品名称搜索,可以给 product_name 加普通索引,但如果你的搜索是LIKE '%牛奶%',普通索引也帮不上忙,因为前缀模糊匹配时索引会失效。这类边界知识,答辩论如果能说出来,会很加分。
三范式在这个阶段也需要动态取舍。第三范式要求消除传递依赖,比如商品表里不应该有供应商名称,因为供应商名称依赖供应商编号,而供应商编号依赖商品编号。但实际做列表展示时,JOIN 供应商表多写一行代码而已,课设数据量小,不必为此冗余字段。如果非要冗余,需要在设计说明里写清楚「这里为了查询性能,故意冗余了供应商名称,通过触发器或应用代码保证一致性」。有设计取舍,比糊里糊涂照抄范式要强得多。
4. 增删改查与并发控制:课程设计最常见的翻车点在写 UPDATE 和锁
建完表,接下来就是最容易被扣分的环节:增删改查。很多课设的 CRUD 代码是从网上复制粘贴的,能跑,但一问原理就露馅。特别是 UPDATE 语句不带 WHERE、事务不提交导致锁表、并发更新互相阻塞,这些是答辩现场最常见的翻车点。
4.1 标准 CRUD 的 SQL 写法:带参数的增删改查与防注入
先看最基本的四条语句,注意其中容易被忽略的细节。
-- 插入一条商品 INSERT INTO product (product_name, purchase_price, sale_price, stock) VALUES ('纯牛奶', 45.00, 68.00, 100); -- 更新库存:必须带 WHERE 条件,否则全表更新 UPDATE product SET stock = stock - 1 WHERE product_id = 10; -- 删除商品:如果被外键引用,直接删除会报错,先删明细再删主表 DELETE FROM purchase_detail WHERE product_id = 10; DELETE FROM product WHERE product_id = 10; -- 查询:按时间段统计销售金额 SELECT DATE(sale_date) AS sale_day, SUM(total_amount) AS daily_revenue FROM sale_sheet WHERE sale_date BETWEEN '2024-01-01' AND '2024-01-31' GROUP BY DATE(sale_date) ORDER BY sale_day;INSERT 语句里列的写出,生产环境里强烈建议写全列名,不要写INSERT INTO product VALUES (...)。因为一旦表结构变了,写全列名的语句还能跑,VALUES 简写会直接报错或者错位。UPDATE 必须带 WHERE,这是写入操作的第一原则。我之前见过一个课设,演示「售出商品」时写的是UPDATE product SET stock = stock - 1,结果点一次按钮,所有商品库存同时减一,全场哄笑。DELETE 也同理,如果商品被进货明细引用,直接 DELETE 会触发外键约束错误,必须先删子表,再删主表。
查询这里用到了 GROUP BY 和 SUM。课设里,「统计」功能如果只做 SELECT * 是拿不到高分的。至少要会写 GROUP BY 按天/按商品分组,会写 SUM/COUNT/AVG。另外还要能用BETWEEN做时间范围过滤。这些是数据库增删改查的基本功。
在应用层,千万不能用字符串拼接 SQL。比如 Python 里cursor.execute("SELECT * FROM product WHERE name = '" + name + "'"),如果 name 传成' OR '1'='1,整张表就会被打出来。这是再基础不过的注入漏洞。正确姿势是使用参数化查询:
cursor.execute( "UPDATE product SET stock = stock - %s WHERE product_id = %s", (quantity, product_id) )参数化查询负责处理转义,就不会有注入问题。课程设计文档里如果能把「为什么参数化」写成理由,会显得你对安全性有意识,答辩时被问住的概率小很多。
4.2 事务与并发锁的边界:为什么两个窗口同时改一条数据会卡死
并发这一块,课设通常不需要做真的高并发压测,但必须能在演示中讲清楚「事务」和「锁」。很多同学的 CRUD 代码里完全没有事务,每一条 INSERT 自动提交,但演示「进货单 + 进货明细」时,如果第二次 INSERT 中途报错,就会造成主表有记录、明细没记录,数据对不上。
-- 典型事务写法:进货单和明细要么都成功,要么都回滚 START TRANSACTION; INSERT INTO purchase_sheet (supplier_id, purchase_date) VALUES (1, '2024-06-01'); SET @sheet_id = LAST_INSERT_ID(); INSERT INTO purchase_detail (sheet_id, product_id, quantity, price) VALUES (@sheet_id, 10, 2, 45.00); INSERT INTO purchase_detail (sheet_id, product_id, quantity, price) VALUES (@sheet_id, 11, 3, 12.50); COMMIT;这段 SQL 里,START TRANSACTION 开始事务,COMMIT 提交。中间如果第二条 INSERT 失败,可以执行 ROLLBACK 把第一条也回滚。LAST_INSERT_ID() 拿到刚插入的自增主键,作为外键值供明细使用。课程设计中的「新增进销存单」功能,都应该是这种事务模式,而不是每一条独立提交。
接下来是并发锁。InnoDB 默认对 UPDATE 的已提交行加行锁。开两个 Navicat 查询窗口,第一个执行:
START TRANSACTION; UPDATE product SET stock = stock - 1 WHERE product_id = 10; -- 此时不提交第二个窗口再执行同样的 UPDATE,你会发现它一直卡着,直到第一个窗口 COMMIT 或 ROLLBACK 才会继续。这就是行锁。如果两个窗口同时执行 UPDATE,一个锁了 product_id=10,另一个锁了 product_id=11,然后互相想更新对方的行,就会发生死锁,MySQL 会自动检测并回滚其中一个事务。演示死锁的方式如下:
-- 事务 A START TRANSACTION; UPDATE product SET stock = stock - 1 WHERE product_id = 10; UPDATE product SET stock = stock - 1 WHERE product_id = 11; COMMIT; -- 事务 B(与 A 并发执行) START TRANSACTION; UPDATE product SET stock = stock - 1 WHERE product_id = 11; UPDATE product SET stock = stock - 1 WHERE product_id = 10; COMMIT;如果两个事务刚好在对方的第一个 UPDATE 之后开始第二个 UPDATE,就会互相等待,最终一条报错「Deadlock found when trying to get lock; try restarting transaction」。课设答辩时,能亲手演示并解释死锁,比背一百个概念都有说服力。
关于锁的类型,答辩高频问题是「乐观锁和悲观锁」。悲观锁就是上面说的SELECT ... FOR UPDATE,先把行锁住再更新;乐观锁靠版本号或时间戳,更新时加一个条件WHERE version = 1,更新成功后再把版本号置为 2。课设里不需要实现太复杂,但你要能画出这张表:悲观锁简单但并发差,乐观锁并发好但冲突多时需要重试。把这些边界知识说清楚,并发锁这道题基本就过了。
5. 避坑指南:从 Navicat 到达梦数据库,兼容性那点事
课设做到一半翻车,很多不是逻辑问题,而是环境问题。一套 SQL 在你的机器上跑得好好的,换到老师电脑上全是报错。这里我整理了五条踩坑记录,每一条都是真实发生过的现象、原因和解决方式。
5.1 本机能跑,换台机器就报错:字符集与数据库版本差异
现象:把 .sql 文件拷贝到另一台电脑用 Navicat 导入,报错一大堆,或者中文全部变成问号。 原因:最常见的是建库时没指定字符集,导致新库用默认 latin1;也可能是源库是 MySQL 5.7,新库是 MySQL 8.0,有些语法不兼容。比如 5.7 里ENGINE=MyISAM还能用,8.0 对某些隐式转换要求更严格。 解决:建库 SQL 里显式写DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;导入前用文本编辑器打开 .sql,看第一行有没有 CHARSET 声明;如果目标库是达梦数据库或者人大金仓这种国产库,记得先查兼容模式,达梦默认区分大小写敏感程度和 MySQL 不一样,字段名大小写问题也会造成「表不存在」的假象。课设里统一用 MySQL 8.0,能省掉大部分环境兼容问题。
5.2 外键约束导致插入顺序颠倒:先主表后从表
现象:先执行进货明细的 INSERT,报「Cannot add or update a child row: a foreign key constraint fails」。 原因:外键要求子表引用记录必须已在父表中存在。你还没插入进货单,就先插明细,明显违反引用完整性。 解决:插入顺序按「主表 -> 从表」来:先 supplier,再 purchase_sheet,再 purchase_detail。如果是从命令行加载大量测试数据,可以在开头写SET FOREIGN_KEY_CHECKS=0;,结尾写SET FOREIGN_KEY_CHECKS=1;。注意这个操作只在会话内有效,不要写进正式业务代码。课程设计报告里要说明「因为外键约束,插入数据必须按照主表到子表的顺序」,这句话本身就是一个得分点。
5.3 自增主键重置了:TRUNCATE 和 DELETE 的区别
现象:把商品表的数据删光以后,再插入新商品,编号不是从 1 开始,而是从上次的最大值 + 1 开始。 原因:DELETE 语句只删数据,不重置 AUTO_INCREMENT 计数器。TRUNCATE 会把表重置,相当于删除所有行并重置计数器。 解决:如果课设需要「清空数据重新演示」,用TRUNCATE TABLE product;会比 DELETE 干净。但 TRUNCATE 不能用于被外键引用的表,否则会报错。稳妥的做法是:先删从表数据,再删主表数据,最后执行ALTER TABLE product AUTO_INCREMENT = 1;重置计数器。这个知识点也是数据库基础面试题里的常客,值得记清楚。
5.4 中文乱码与排序规则
现象:插入一条中文商品名称,SELECT 查出来是问号「???」,或者插入直接报「Incorrect string value」。 原因:字符集链路没打通。数据库是 utf8mb4,但连接字符串用的字符集是 latin1,或者字段本身就是 latin1。 解决:Navicat 连接时在「连接属性 -> 编码」里选择 utf8mb4;Java JDBC 连接串加useUnicode=true&characterEncoding=utf8;Python 的 MySQLdb 连接参数加charset='utf8mb4'。建表时统一用 utf8mb4,排序规则用utf8mb4_general_ci还是utf8mb4_unicode_ci在课设体量下没差别,但你要能区分:unicode_ci 按 Unicode 标准排序,更精确但稍慢;general_ci 是 MySQL 的特殊优化,更快但排序规则有少量例外。记住这一点,答辩时可进可退。
5.5 课设文档里的 SQL 与运行脚本不一致:报告写得再好,演示翻车最常见
现象:答辩时老师照着报告里的表结构提问,但实际数据库里的 product 表缺少一个「备注」字段,程序运行也报错。 原因:报告改了三版,SQL 脚本改了两版,最后忘更新文档。或者说是演示前临时在数据库里加了字段,但没有同步到报告。 解决:把最终运行成功的建库、建表、初始数据脚本统一整理成一个init.sql,放在课程设计目录下,报告中的表结构、关系模式、ER 图、数据字典全部以这个脚本为准。演示前,在一台干净环境里重新执行一遍 init.sql 和核心 CRUD 脚本,不要用你自己电脑上已经改得乱七八糟的库。这条流程是我带课设时反复强调却最容易忽略的一条,翻车率极高。
6. 把设计文档变成答辩底稿:用数据量压力测试验证你的索引和锁
课程设计的最后一道坎是答辩。你写了很多页文档,但如果面对「你的索引有效吗」「并发会死锁吗」「数据多了会不会变慢」这三个问题答不上来,前面的努力会大打折扣。我的习惯是,在交文档前主动做一轮数据量验证。
先造测试数据。用存储过程往商品表里插一万行,往销售明细里插十万行,再跑一次聚合查询,观察耗时和 EXPLAIN 的输出。如果发现某条 SELECT 走了全表扫描,就在关键字段上补索引。索引不是布置作业,是要用数据证明它有价值。
-- 造数据的简单办法:递归 CTE 在 MySQL 8.0 中可用 INSERT INTO product (product_name, purchase_price, sale_price, stock) SELECT CONCAT('测试商品', n), RAND()*50, RAND()*100, 100 FROM ( WITH RECURSIVE seq AS (SELECT 1 AS n UNION ALL SELECT n+1 FROM seq WHERE n < 10000) SELECT n FROM seq ) t;造完数据后,用EXPLAIN SELECT ...看 key 字段是不是你建的索引。如果索引没生效,多半是 WHERE 条件里对索引列做了函数处理,比如WHERE YEAR(sale_date) = 2024,这时候改成WHERE sale_date BETWEEN '2024-01-01' AND '2024-12-31'就能让索引生效。这类优化技巧,正好也是数据库优化和面试题里最常问到的点。
并发锁的验证更直接:开两个查询窗口,故意制造一次死锁,然后把报错信息截图放进文档的「异常处理」一节。这不是自爆短板,而是展示你理解锁的边界。很多优秀课设都会有「问题与解决」部分,死锁演示比「所有功能都正常」更能体现水平。最后,做一次 mysqldump 备份,再把备份文件导入到新库,确认数据不缺失。这一套流程走下来,你手上既有报告,也有可复现的脚本和测试记录,答辩时心里不虚。
我自己的习惯是:每次课设都保留一份「演示前检查清单」,从建库脚本执行,到核心 CRUD 跑通,再到并发演示和备份恢复,逐项打勾。这份清单救过我很多次,因为数据库这门课最怕的不是不会写,而是写完了才发现环境不一致。希望帮到你。
本文还有配套的精品资源,点击获取