简介:南京航空航天大学人工智能专业2024年《数据库原理》课程设计项目是一份完整的课程实践资源包,压缩包内共含6个文件,包括2个SQL脚本(建库脚本与运行脚本)、1个Python程序、1份PDF上机实验报告、1个README说明文件及1个txt文本文档,整体仅1.92MB,便于下载与浏览。这份资源围绕数据库原理课程的核心任务展开,既涉及数据库表结构定义、数据增删改查、索引与触发器,又通过Python脚本演示了数据库编程接口的调用,覆盖SQL语言、数据模型、关系规范化、查询优化、事务管理等重要知识点。对于正在学习数据库原理或需要完成类似课程设计的人工智能专业学生,可以直观展示从需求分析、概念设计、逻辑设计、物理实现到测试维护的完整流程,帮助理清设计脉络,借鉴南航的教学要求与实现方式。实验报告详细记录了上机实践过程与运行结果,配合可直接运行的脚本,便于读者对照验证或二次开发。目前已有212人学习,是理解数据库课程设计规范、快速搭建实验环境的实用参考。
1. 数据库课程设计完整工程包:从建库脚本到演示代码,先跑通再谈加分
某高校人工智能专业《数据库原理》课程设计,交上去的是这样一个压缩包:SQL 脚本、Python 主程序、依赖清单、实验报告 PDF,外加一份 README。很多同学拿到这类工程包的第一反应是打开 README 找运行步骤,结果发现文档只写了「环境要求」四个字。这套资源的价值不在于代码量有多大,而在于它把一门数据库课设要交的东西全部按工程方式组织好了——从 setup.sql 建库建表,到 run.sql 演示查询,再到 main.py 把 SQL 串成业务逻辑。适合两类人:一是准备做数据库课设但没想清楚整体结构的学生,二是想复现一套完整「建库—写代码—出报告」流程的初学者。先说明一点,这套工程最常见的载体是学生选课成绩管理这一类小型系统,下文全部按这个场景展开。
2. 先看懂工程里的 SQL 文件:setup.sql、run.sql 的分工与执行顺序
2.1 setup.sql 承担了什么:建表、约束与初始数据
大多数课设包里的 setup.sql 负责的是数据库的「地基」:创建数据库、建立数据表、定义主键外键、写入初始数据。以学生选课成绩管理系统为例,你会在 setup.sql 里看到这三类核心表的定义逻辑:
CREATE DATABASE IF NOT EXISTS db_course DEFAULT CHARACTER SET utf8mb4; USE db_course; CREATE TABLE student ( sno CHAR(10) PRIMARY KEY, sname VARCHAR(20) NOT NULL, ssex CHAR(2) DEFAULT '男', sage TINYINT ); CREATE TABLE course ( cno CHAR(6) PRIMARY KEY, cname VARCHAR(40) NOT NULL, ccredit DECIMAL(3,1) ); CREATE TABLE score ( sno CHAR(10), cno CHAR(6), grade DECIMAL(5,2), PRIMARY KEY (sno, cno), FOREIGN KEY (sno) REFERENCES student(sno), FOREIGN KEY (cno) REFERENCES course(cno) );这段代码的逻辑很直白:student 表用学号做主键,course 表用课程号做主键,score 表是典型的选课关系表,用 (sno, cno) 联合主键,同时把 sno 和 cno 设为外键。参数上有两个细节值得注意。第一个是 DEFAULT CHARACTER SET utf8mb4,如果建库时不指定字符集,MySQL 会走默认配置,插入中文姓名时大概率会变成一串问号。第二个是 sage 用 TINYINT,选课成绩系统的学生年龄范围不会超过 127,用 INT 纯属浪费空间——课程设计评审里对「数据类型选取得当」是有印象分的。
运行 setup.sql 之后,数据库里应该能看到完整的表结构和初始数据。建议跑完立刻用 SHOW TABLES 确认表数量,再用 SELECT COUNT(*) 确认每张表的行数,这是判断脚本有没有半途报错的最快方式。
2.2 run.sql 是演示脚本:查询、视图与事务的编排
run.sql 在课设包里的角色是「给评审老师看的演示稿」。它不会像 setup.sql 那样做大量 DDL,而是把课程设计报告里写的那些核心查询、视图、事务操作按顺序排好,保证老师或助教执行一遍就能看到所有加分项。
-- 查询每门课程的选课人数和平均分 SELECT c.cname, COUNT(s.sno) AS stu_count, AVG(sc.grade) AS avg_grade FROM course c LEFT JOIN score sc ON c.cno = sc.cno LEFT JOIN student s ON sc.sno = s.sno GROUP BY c.cno, c.cname ORDER BY avg_grade DESC; -- 创建视图:显示成绩低于60分的学生信息 CREATE OR REPLACE VIEW v_failed AS SELECT s.sno, s.sname, c.cname, sc.grade FROM student s JOIN score sc ON s.sno = sc.sno JOIN course c ON sc.cno = c.cno WHERE sc.grade < 60;这里的核心是 JOIN 的方向。LEFT JOIN 配合 GROUP BY 可以查出「还没人选修的课程」——如果某门课的选课人数是 0,用 INNER JOIN 会把这门课整行过滤掉,严重时会导致排序结果和报告对不上。CREATE OR REPLACE VIEW 后面通常还会跟几条 SELECT 验证视图数据,如果你拿到的 run.sql 只有视图定义没有验证语句,自己补上两行 SELECT * FROM v_failed,演示效果会完整很多。
除了查询和视图,run.sql 里通常还会有一段事务操作,常见的形态是模拟选课扣减容量:开启事务、插入 score 记录、检查是否冲突、正常则提交否则回滚。执行它的时候重点关注 COMMIT 和 ROLLBACK 的分支是否都在,缺了任何一条,事务演示都是不完整的。
2.3 拿到脚本后第一步:按依赖顺序在 MySQL 里跑通
一个最常见的翻车点是把 run.sql 在 setup.sql 之前执行,或者在 setup.sql 还没跑完时就急着看后面的结果。正确的执行顺序是这样的:
mysql -u root -p < setup.sql mysql -u root -p < run.sql从这里能看出两个工程细节。第一,setup.sql 里 CREATE DATABASE 和 CREATE TABLE 有严格的先后依赖,少了前者后者必然报错;第二,run.sql 里的查询依赖 setup.sql 写入的数据,必须先有数据才能出结果。如果你用的是图形化客户端,就按文件名逐个打开执行,别用「全部运行」一把梭,否则报错信息会淹没在同一条执行日志里,定位问题得翻半天。「先建库、再演示、最后跑代码」是这套工程包唯一的正确顺序。
执行完后做一个快速自查:SHOW TABLES 能看到三张以上的业务表,SELECT 能返回非空结果集,视图能正常查询。走到这一步,数据库侧的工作就已经通了。
3. main.py 不是玩具代码:连接管理、参数化查询与业务分层
3.1 数据访问层怎么写:连接对象、游标与关闭逻辑
main.py 是这个工程包里 Python 与 MySQL 之间的桥。课设级别的代码,连接部分常见用的是 pymysql,连接参数大概率写在一个函数里,方便其他地方反复调用。
import pymysql def get_conn(): conn = pymysql.connect( host="127.0.0.1", port=3306, user="root", password="your_password", database="db_course", charset="utf8mb4", cursorclass=pymysql.cursors.DictCursor ) return conn这段代码的关键参数要逐个说清楚。host 和 port 默认是本机 3306,如果连接的是 Docker 容器里的 MySQL,host 要改成容器映射出来的地址。charset 必须和 setup.sql 里的 utf8mb4 保持一致,否则 Python 写入中文时会在编码转换上报错。cursorclass 用 DictCursor 会让查询结果以字典形式返回,输出时可以直接用 row["sname"],比默认的元组取值友好得多,课设报告里的打印代码也因此会短一截。
任何连接对象都必须有对应的关闭动作。很多初写的 main.py 只开不关,程序跑完连接池耗尽,第二次运行就报 Too many connections。常见做法是每次用完后在 finally 块里同时关闭游标和连接,写成结构化的上下文管理器形式更稳。
3.2 业务逻辑层到控制台菜单:增删改查的完整闭环
课设的 main.py 通常长这样:一个 while 循环打印菜单,用户输入数字选择功能,每个分支调用一个函数,函数内部执行 SQL 并打印结果。以学生信息管理为例,插入和查询的核心逻辑是这样组织的:
def add_student(conn): sno = input("请输入学号: ") sname = input("请输入姓名: ") ssex = input("请输入性别: ") sage = int(input("请输入年龄: ")) with conn.cursor() as cur: cur.execute( "INSERT INTO student (sno, sname, ssex, sage) VALUES (%s, %s, %s, %s)", (sno, sname, ssex, sage) ) conn.commit() print("学生信息添加成功") def query_student(conn): keyword = input("请输入姓名关键字: ") with conn.cursor() as cur: cur.execute( "SELECT sno, sname, ssex, sage FROM student WHERE sname LIKE %s", (f"%{keyword}%",) ) rows = cur.fetchall() for r in rows: print(r["sno"], r["sname"], r["ssex"], r["sage"])这两段代码体现了课设代码和玩具代码的分水岭。第一,参数全部用 %s 占位符传参,杜绝了直接字符串拼接 SQL 的写法,这一点在答辩时经常被问到,回答「防止 SQL 注入」比「别人都这么写」体面得多。第二,INSERT 之后在连接对象上调 conn.commit(),pymysql 默认 autocommit 是 False,不写 commit 数据不会真正落库,重启程序后数据消失,这是最常见的低级事故。第三,LIKE 查询的模糊匹配关键字也要通过参数传入,百分号拼在参数内部,SQL 语句本身保持干净。
菜单部分通常是 while True + if/elif 的结构,每个分支调用一个函数。需要注意的是退出分支必须 break 之前关闭连接对象,否则 Ctrl+C 强制退出后连接会滞留在数据库侧。
3.3 从课设代码到可评审代码:注释、异常与输出格式
评审老师打开 main.py 时,不会一行行数代码量,但会看三个地方:有没有处理异常、有没有写清楚注释、控制台输出是不是人能看明白的格式。这三个点都不难做,但很多工程包恰恰缺在这里。
异常处理最值得补的是连接失败和 SQL 执行失败两个场景:
try: conn = get_conn() except pymysql.err.OperationalError as e: print("数据库连接失败,请检查 MySQL 服务是否启动") print(f"错误码: {e.args[0]},错误信息: {e.args[1]}") exit(1)e.args 里装的是 MySQL 报错的错误码和描述信息,比如 1045 是密码错误,2002 是端口不通或服务没起。把这两个参数打出来,排查问题的时候比只看「连接失败」四个字高效太多。注释方面不需要每行都写,但每个函数上方的三行 docstring 是值得留的,写清楚函数做什么、入参是什么、返回什么,报告里贴代码的时候也方便直接裁剪。输出格式上,查询结果打印前先打一行字段名分隔线,比直接堆元组好看得多,这一条能从观感上直接把代码档次拉高一分。
4. 运行前先治理环境:requirements.txt、Python 版本与依赖安装顺序
4.1 requirements.txt 里常见依赖与版本陷阱
这个工程包里的 requirements.txt 篇幅不长,但内容直接决定 main.py 能不能跑起来。以 pymysql 为核心的课设,依赖清单常见长这样:
pymysql>=1.0.2 cryptography>=3.4.8 pandas>=1.3.0三个依赖各有用处。pymysql 是数据库驱动,负责 Python 与 MySQL 之间的通信;cryptography 在 MySQL 8.0 以上版本几乎必须装,因为新版 MySQL 默认的 caching_sha2_password 认证插件需要它来做加密握手,不装就会在连接时直接报认证失败;pandas 不是必需品,但报告里如果涉及成绩分布统计、平均分对比这类数据分析图表,main.py 里大概率会用它辅助处理查询结果。
版本陷阱集中在 pymysql 上。1.0.2 以上版本对 Python 3.8 以下不再提供支持,如果你用的是 Python 3.6 的老环境,pip 安装时会直接报找不到匹配版本。这时候两个选择:升级 Python 到 3.8+,或者把 pymysql 版本降到 0.10.1。我的建议是前者,降版本虽然能装上,但老版本对 utf8mb4 和 DictCursor 的支持都不如新版稳。
4.2 安装依赖与初始化数据库的一体化流程
复现这套工程包的完整顺序应该是:先建虚拟环境,再装依赖,然后初始化数据库,最后跑 main.py。命令行下就是这么一串:
python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install -r requirements.txt mysql -u root -p < setup.sql mysql -u root -p < run.sql python main.py这里的顺序有一个容易忽略的细节:先装依赖再导 SQL。因为 main.py 里的连接参数(尤其是密码)可能要改,而改完密码后的第一件事就是重新验证连接,如果 SQL 侧的表还没建好,连接测试会卡在「数据库不存在」这个错误上。反过来,如果先建库再装依赖,万一依赖安装失败,数据库已经建了一半,排错时还得顾两边,状态太乱。
虚拟环境这一步在任何机器上都值得做。直接用系统 Python 装 pymysql 也不是不能跑,但机器上多个项目共存时,版本冲突迟早出问题。venv 把依赖隔离在当前目录,删掉重来也就一条 rm -rf venv 的事。
4.3 README 没写全时,怎么从文件倒推运行逻辑
很多课设包的 README 只有寥寥几句环境要求,拿到手根本没法直接照做。这时候看文件依赖关系比看文档更可靠,判断顺序只有两条。第一,从文件命名判断执行顺序:setup 开头的永远是最先执行的,run 开头的紧随其后,main 结尾的 Python 文件最后运行。第二,从 import 语句判断依赖层次:打开 main.py 看顶部 import 了哪些库,再去 requirements.txt 里核对是否存在,缺谁装谁,不必一次性全装完。
文件包里那份实验报告 PDF 也是重要的信息来源。报告里的「系统运行截图」「数据库设计说明」章节几乎必然包含表结构、SQL 和运行结果,技术上先参考报告再回看代码,比对着空代码盲猜快得多。这套流程走下来,即使 README 只写了三行,工程包也能在十分钟内跑通。
5. 避坑:把这套工程复现三遍之后总结出的五个问题
5.1 现象:setup.sql 执行时报错,错误码 1064,提示 syntax error
原因:文件编码不是 UTF-8。Windows 下用记事本保存的 SQL 文件经常是 GBK 编码,MySQL 客户端按 utf8mb4 解析时,中文字符串的字节序列触发语法解析错误。
解决:用 VS Code 或任意编辑器打开 setup.sql,右下角查看编码格式,另存为 UTF-8;或者在 mysql 命令行执行前先执行 SET NAMES utf8mb4。我一般直接把所有 SQL 文件统一转成 UTF-8 再入库,一劳永逸。
5.2 现象:setup.sql 单独执行成功,放到工程包里再执行就报外键约束错误,错误码 1215
原因:表之间的创建顺序不对。score 表引用了 student 和 course,如果先执行 score 的建表语句,而被引用的表还不存在,外键约束自然建立失败。
解决:打开 setup.sql 检查 CREATE TABLE 顺序,先建被引用的表,再建引用表。如果脚本里顺序已经乱掉,把外键约束从建表语句里拆出来,单独放到文末用 ALTER TABLE 补上。更简单的办法是把三张表的建表语句按依赖顺序重排一遍。
5.3 现象:main.py 连接数据库时报错,提示 Authentication plugin 'caching_sha2_password' cannot be loaded
原因:pymysql 与 MySQL 8.0 的加密握手不兼容,缺少 cryptography 依赖库。
解决:执行 pip install cryptography 后重启程序。如果不想装额外依赖,也可以在 MySQL 里把账号认证方式改回 mysql_native_password,但我的建议是装 cryptography,改认证方式会影响数据库安全性。
5.4 现象:run.sql 执行结果和实验报告里的截图数据对不上,行数或数值不一致
原因:报告是某个时间点生成的,但 setup.sql 的初始数据后来被改过,或者 run.sql 在执行过程中用了 INSERT/UPDATE 修改了数据。
解决:对比报告截图和 run.sql 查询条件里的常量数值,如果 WHERE 条件写的是具体学号或课程名,直接用 SELECT 去库里核对。更稳妥的流程是:跑 run.sql 前先重新执行一遍 setup.sql,把数据恢复到初始状态,保证报告、脚本、库三者完全一致。
5.5 现象:main.py 查询中文正常,但新增或修改中文数据时乱码
原因:连接参数的 charset 与数据库字符集不一致。数据库是 utf8mb4,连接却是 utf8,写入时如果遇到表情符号或生僻字就会失败或乱码。
解决:把连接参数统一改成 charset="utf8mb4",同时确认 setup.sql 建库语句里的 DEFAULT CHARACTER SET 也是 utf8mb4。三处一致——库、连接、客户端——中文问题基本绝迹。
6. 答辩前最后一个动作:把实验报告重新跑成一份「活的证明」
很多课设的最大风险不是代码写了多少,而是报告是报告、代码是代码,两套东西对不上。答辩前最值得花时间的一步,就是拿 run.sql 和 main.py 做一次回归验证,把报告里的每一项结论重跑一遍。我的习惯是按章节对:报告写了「平均分最高的课程」,我就跑对应的 GROUP BY 查询,把结果里第一行的课程名核对一遍;报告贴了某个菜单功能的截图,我就实际操作一遍 main.py 的对应选项,确认打印字段和截图一致。任何一处对不上,都改代码或改报告,但绝不能两个都放着不动。
报告里那些「系统测试」「性能分析」章节,补起来也有取巧路径。把 run.sql 里几个核心查询的执行时间量出来,用 SELECT 结果的行数说明查出了多少有效记录,再对比加索引前后同一查询的耗时差异,这三条证据就足以支撑起一个完整的测试章节。加索引的对比我一般这样做:
EXPLAIN SELECT * FROM score WHERE sno = '20240001'; CREATE INDEX idx_score_sno ON score(sno); EXPLAIN SELECT * FROM score WHERE sno = '20240001';执行计划里 rows 字段从全表扫描的数值下降到接近 1,就是一条能直接写进报告的结论。这个技巧不花一分钟,但对答辩评分的「优化能力」维度几乎是决定性证据。
从那以后我再做任何数据库课设,都强制自己留出半小时做这一遍回归验证,顺手把 EXPLAIN 的结果存进报告附录。这半小时从来不亏,含糊不清的地方全在答辩前暴露出去了。希望帮到你。
本文还有配套的精品资源,点击获取