简介:面向数据库课程设计场景的健康档案管理系统文档,适合计算机、软件工程等专业学生及数据库初学者参考。内容完整覆盖数据库设计的核心步骤,从需求分析中的数据流图与数据字典,到概要结构设计中的功能模块划分,再到逻辑结构设计中的ER图转换和SQL建表,系统展示如何构建一个便于医疗人员、患者及相关机构访问分析的健康信息管理平台。资源以doc格式提供,共1个文件,压缩包大小863KB,内容结构清晰,包含课程设计目的、需求分析、概要设计和逻辑结构设计等章节。目前已有128人学习下载。通过研读可掌握用户管理、档案录入、查询检索、统计分析等模块的数据库建模方法,理解数据规范化与索引优化的实际应用,也可作为课程设计报告撰写的范本。
1. 这份健康档案管理系统课程设计:从数据流图到建表SQL的完整模板
数据库课程设计是很多学校数据库课的硬性实践环节,而《数据库课程设计——健康档案管理系统》这份文档,就是一套可以直接照着改的学生健康档案管理系统完整报告。系统面向在校学生,把健康数据拆成病历文件和体检文件两类,登记、修改、删除、查询、统计五大功能把数据库增删改查的典型操作全部覆盖了一遍。对正在赶课程设计的人来说,它最值钱的部分不是最后的结论,而是从数据流图、数据字典一路推到概要结构、逻辑结构、物理结构再到建表SQL的完整推进路径。新手照着这份文档能顺完整个设计流程,熟手则可以复用它的统计数据模型,按自己学校的字段要求替换。这也决定了下面的拆解重点:不只是看它写了什么,更要说清楚每一步为什么这么做、改的时候会踩哪些坑。
2. 需求分析:五类功能与两类数据文件怎么拆成数据流图和数据字典
需求分析这一章在任何课程设计报告里都是最容易凑字数、也最容易被老师挑毛病的部分。这份文档的做法是先把功能要求列清楚,再用数据流图和数据字典把数据和加工定死。顺序很重要:先有功能边界,才能画数据流图;先有数据流图,才能写数据字典。
2.1 五类功能拆到SQL:登记、修改、删除、查询、统计的边界
功能要求原文写得很朴素:登记是“将学生的健康信息插入健康文件”,修改是“修改一个学生的健康档案记录”,删除是“删除学生的健康档案记录”,查询是“组合各种条件进行查询”,统计是“对学生的基本健康状况进行各种必要的统计和分析”。落到数据库操作上,这套功能正好是一组标准的增删改查加聚合查询。
| 功能 | 对应SQL操作 | 关键点 |
|---|---|---|
| 登记 | INSERT INTO ... | 学号唯一性校验,重复插入要报错 |
| 修改 | UPDATE ... WHERE student_id = ? | 必须先定位到具体学生,不能漏WHERE |
| 删除 | DELETE WHERE student_id = ? | 注意外键关联的体检、病历记录 |
| 查询 | SELECT ... WHERE 条件1 AND 条件2 | 支持组合条件,输出报表 |
| 统计 | GROUP BY + COUNT/AVG/窗口函数 | 分一般统计和动态分析两类 |
比较容易被忽视的是“修改”操作。很多初写课程设计的人会写出不带WHERE的UPDATE,直接把整张表改掉。这里的正确逻辑是先按学号定位再更新,最好在界面上先查出原记录、再改、再提交,对应SQL就是按主键更新。登记操作则要在写入前先检查学号是否已存在,常见的做法是先用SELECT查一次,或者直接依靠主键冲突捕获异常。
查询部分原文特意强调了“组合各种条件”,这个要在SQL里体现出来。经典写法是等值条件加范围条件用AND连接,比如查某个学生在某个时间段内的体检记录:
SELECT * FROM physical_exam WHERE student_id = '20230001' AND exam_date BETWEEN '2023-09-01' AND '2024-06-30';这段SQL的逻辑是先用学号做等值过滤,再用日期范围收窄结果集。如果还想加系别,就要JOIN学生表,把系别条件放到JOIN后的WHERE里。注意BETWEEN是闭区间,两个端点日期都会被包含,如果你不想包含边界日期,就改用exam_date >= '2023-09-01' AND exam_date < '2024-07-01'。这个细节写进文档里,比单纯画一张流程图更能让老师相信你是真做过。
2.2 数据流图:从“原始数据”到“反馈信息”的两层加工模型
数据流图是需求分析的核心产出,但这门课里老师普遍不会要求画到多细,关键是层级关系要成立。文档里给出的数据流名称是“原始数据”和“反馈信息”,来源是医务室,去向下一个环节是健康档案管理系统。按照常见做法,我会画两层:
顶层图只有三个元素:外部实体“医务室”,加工“健康档案管理系统”,以及两条数据流。“原始数据”从医务室流向系统,里面包含学号、姓名、性别、系别、年龄、身高、体重、胸围、日期、诊断结果、联系方式、医疗记录、是否住院等字段;系统处理完以后,把操作结果和统计报表作为“反馈信息”返回医务室。
0层图再把“健康档案管理系统”这个加工细化成五个加工,分别是登记、修改、删除、查询、统计,对应数据存储是“体检文件”和“病历文件”。每个加工的输入输出可以列成一张表:
| 加工名称 | 输入数据流 | 输出数据流 | 读写数据存储 |
|---|---|---|---|
| 登记 | 原始体检/病历数据 | 登记成功/失败提示 | 写体检文件、病历文件 |
| 修改 | 学号 + 更新字段 | 更新成功/失败提示 | 读并写体检文件、病历文件 |
| 删除 | 学号 | 删除成功/失败提示 | 读并删除体检文件、病历文件 |
| 查询 | 组合条件 | 学生健康信息报表 | 读体检文件、病历文件 |
| 统计 | 统计范围与类型 | 统计报表 | 读体检文件、病历文件 |
提示:课程设计的DFD不要求画到每个字段,但数据字典里出现的字段必须能在数据流里找到出处。否则答辩时很容易被老师指出来“这个字段从哪进来的”,当场翻车。
画DFD还有个容易被忽略的细节:加工编号。登记是1,修改是2,删除是3,查询是4,统计是5。编号不需要多复杂,但它能清楚表达加工之间的先后关系和优先级,写文档时引用起来也更方便。
2.3 数据字典与字段清单:两类文件的属性取舍和扩展设计
数据字典是需求分析里最枯燥但也最不能省的部分。文档中数据流条目“原始数据”的组成写得比较全:学号、姓名、性别、系别、年龄、身高、体重、胸围、日期、诊断结果、联系方式、医疗记录、是否住院、其他。这里有个不太明显的问题:年龄、身高、体重、胸围是体检数据,诊断结果是病历数据,联系方式是学生基础信息。三类字段混在一条数据流里,在后面的逻辑设计阶段必须拆分。
字段类型和长度建议按下面的表格定,这个表可以直接复用进文档的数据字典部分:
| 数据项 | 类型 | 长度/精度 | 取值范围 | 说明 |
|---|---|---|---|---|
| 学号 | 字符型 | 20 | 字母或数字 | 学生唯一标识,不用数值型 |
| 姓名 | 字符型 | 50 | 中文或字母 | 学生姓名 |
| 性别 | 字符型 | 1 | M/F | 存代号不存中文 |
| 系别 | 字符型 | 50 | 系名 | 关联学生主表 |
| 年龄 | 数值型 | 3 | 1-150 | 放在体检记录里,随时间变化 |
| 身高 | 数值型 | DECIMAL(5,2) | 单位cm | 不用FLOAT避免浮点误差 |
| 体重 | 数值型 | DECIMAL(5,2) | 单位kg | 同上 |
| 胸围 | 数值型 | DECIMAL(5,2) | 单位cm | 同上 |
| 日期 | 日期型 | DATE | YYYY-MM-DD | 体检或诊断日期 |
| 诊断结果 | 字符型 | 200 | — | 病历记录核心字段 |
| 医疗记录 | 文本型 | TEXT | — | 病历扩展字段 |
| 是否住院 | 布尔型 | TINYINT(1) | 0/1 | 0否1是,不用ENUM |
年龄放进体检记录而不是学生表,这个决定要能解释清楚:年龄每年都在变,它属于“某次体检时”的快照属性,不属于学生不变信息。同理,身高、体重、胸围也是快照。只有姓名、性别、系别这些相对稳定的学生属性才该进学生主表。诊断结果用VARCHAR(200)是给常见疾病诊断留余量,如果遇到特别长的描述,就归入TEXT字段的医疗记录。是否住院用0/1两个值足够了,课程设计没必要引入ENUM,省得以后扩展值域时还要改表结构。
3. 逻辑结构设计:从ER图到三张核心表的建表SQL
逻辑结构设计是把需求分析里的数据字典变成关系模式的关键一步。这份文档的顺序是先把实体和联系画成ER图,再转成关系模式,最后落成SQL。三步每一步都有容易出错的地方。
3.1 ER图转关系模式:学生、体检记录、病历记录怎么映射成表
系统涉及三个实体:学生、体检记录、病历记录。学生实体的属性是学号、姓名、性别、系别、联系方式;体检记录实体的属性是体检编号、学号、体检日期、年龄、身高、体重、胸围、体检项目;病历记录实体的属性是病历编号、学号、诊断日期、诊断结果、医疗记录、是否住院。联系是两条一对多:一个学生对应多次体检记录,一个学生对应多份病历记录。
ER图转关系模式有一条固定规则:一对多联系中,“一”端的主键并入“多”端作为外键。所以学生与体检记录的联系,体现在体检记录表里加一个学号外键;学生与病历记录的联系,体现在病历记录表里加一个学号外键。关系模式可以写成:
- 学生(学号,姓名,性别,系别,联系方式)
- 体检记录(体检编号,学号,体检日期,年龄,身高,体重,胸围,体检项目)
- 病历记录(病历编号,学号,诊断日期,诊断结果,医疗记录,是否住院)
下划线字段是主键,学号在两张子表里都是外键。体检编号和病历编号用自增整数,理由是所有业务操作都是按学号来找记录的,编号只是内部主键,不需要暴露给用户。
这个映射过程要在文档里写清楚“每个实体对应哪张表、每个联系对应哪个外键”,不能只贴一张ER图。老师看逻辑设计,重点就是看你能不能把图上的关系讲成表之间的约束。
3.2 建表SQL:三张表的主键、外键与约束一次写对
建表顺序有讲究:必须先建学生表,再建带外键的两张记录表,否则MySQL直接报“外键依赖表不存在”。三张表的建表语句如下:
-- 学生主表:存放学生基础信息 CREATE TABLE student ( student_id VARCHAR(20) NOT NULL COMMENT '学号', name VARCHAR(50) NOT NULL COMMENT '姓名', gender CHAR(1) NOT NULL COMMENT '性别:M男 F女', department VARCHAR(50) NOT NULL COMMENT '系别', phone VARCHAR(20) DEFAULT NULL COMMENT '联系方式,可空', PRIMARY KEY (student_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生基本信息表';-- 体检记录表:每次体检生成一条记录 CREATE TABLE physical_exam ( exam_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '体检记录编号', student_id VARCHAR(20) NOT NULL COMMENT '学号,外键', exam_date DATE NOT NULL COMMENT '体检日期', age INT NOT NULL COMMENT '体检时年龄', height DECIMAL(5,2) NOT NULL COMMENT '身高,单位cm', weight DECIMAL(5,2) NOT NULL COMMENT '体重,单位kg', chest_circ DECIMAL(5,2) NOT NULL COMMENT '胸围,单位cm', project_name VARCHAR(100) DEFAULT NULL COMMENT '体检项目名称,扩展用', CONSTRAINT fk_exam_student FOREIGN KEY (student_id) REFERENCES student(student_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='体检记录表';-- 病历记录表:每次诊断生成一条记录 CREATE TABLE medical_record ( record_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '病历编号', student_id VARCHAR(20) NOT NULL COMMENT '学号,外键', diagnosis VARCHAR(200) NOT NULL COMMENT '诊断结果', diagnosis_date DATE NOT NULL COMMENT '诊断日期', medical_note TEXT COMMENT '医疗记录,可空', is_hospitalized TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否住院:0否1是', CONSTRAINT fk_medical_student FOREIGN KEY (student_id) REFERENCES student(student_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='病历记录表';这里有几个参数值得在文档里单独说明。学号用VARCHAR(20)而不是INT,是为了保留学号里的前导零和字母,很多学校的学号是“2023xxxxx”这类格式,用数值类型会丢信息。身高体重胸围用DECIMAL(5,2),5位总长度、2位小数,足够存矮到高所有正常范围,又不至于像FLOAT那样出现二进制浮点误差。性别用CHAR(1)存M/F,不直接存中文,是为了避免字符集不同导致后续查询条件写错。外键列类型必须与主表主键完全一致,这里两边都是VARCHAR(20),如果写成INT就会报错误1215。
medical_note和project_name都允许为NULL,这是故意的。课程设计的数据量小,不需要把所有字段都填满;更重要的是,原文里提到体检文件里还有体检项目和医疗记录,这两个字段是给系统未来扩展用的,允许为空不会影响主流程,反而让表结构更灵活。
3.3 规范化检查:为什么把姓名、系别从体检表里拆出来
原始需求里体检文件的字段是学号、姓名、性别、系别、年龄、身高、体重、胸围、日期。如果直接按这个建体检表,每个学生体检一次就重复一遍姓名性别系别。同一个学生连续体检五年,就要存五份重复信息,这就是典型的冗余。
用规范化理论过一遍就很清楚。第一范式要求属性不可再分,这个表满足。第二范式要求非主属性完全依赖主键,但“姓名、性别、系别”其实只依赖学号,不依赖体检编号,属于“部分依赖”,不满足第二范式。第三范式要求消除传递依赖,系别这个属性在学生主表里已经存在,体检表里再放一份就是冗余。所以拆分是必须的。
拆完以后,学生表存姓名、性别、系别,体检表只存学号外键和体检当次的快照数据。这样改姓名只需要改学生表一条记录,按系别统计直接从学生表JOIN体检表就能做。在答辩时如果老师问“为什么体检表里没有姓名”,就用这条规范化逻辑回答,比“我觉得该拆”要站得住。如果你学校要求报告里必须保留原始字段清单,可以先写原始需求字段,再写规范化后的拆分结果,说明这是“从用户视角到系统视角的转换”,这是课程设计里的常用写法,不矛盾。
4. 物理结构设计:索引、存储引擎与EXPLAIN验证的三个平衡点
物理结构设计在这份文档里篇幅不大,但恰恰是“能不能跑得快”的分水岭。课程设计的数据量通常很小,索引设计往往被忽略,但老师喜欢抽查的就是这一块:存储引擎用的什么、索引建在哪、为什么这么建。
4.1 索引设计:学号、日期、系别三个维度的联合索引组合
系统的核心查询有两个:查某学生的历年体检记录、查某学生的历年病历记录。所以两个最关键的索引都围绕学号加日期来建:
CREATE INDEX idx_exam_student_date ON physical_exam(student_id, exam_date); CREATE INDEX idx_medical_student_date ON medical_record(student_id, diagnosis_date);这里选择建立联合索引而不是两个单列索引,是因为查询条件通常是“学号 + 日期范围”一起出现。联合索引需要遵循最左前缀原则:student_id放在最左边,查询中只要带有学号,这个索引就能被使用;即使不查日期,单独用学号也能走这个索引。如果反过来把exam_date放前面,那么单独按学号查询时索引就帮不上忙了。
系别维度的统计查询不单独建索引,因为它走的是学生表JOIN体检表的路径,学生表主键就是学号,系别字段上的条件只影响JOIN后的过滤,数据量小的时候全表扫描代价可以接受。真正要避免的是给体检表里每个字段都建索引,索引太多会让INSERT变慢,还会占用额外磁盘空间,这是新手最容易犯的过度设计。
4.2 存储引擎与建表参数:InnoDB、utf8mb4和事务的选择
这节课设计如果用的是MySQL,存储引擎直接用InnoDB,不要选MyISAM。原因很具体:这个系统有登记、修改、删除操作,InnoDB支持事务和外键,MyISAM不支持。万一删除学生记录时程序中途报错,InnoDB可以回滚,MyISAM可能会留下半删除状态的数据。外键约束就更不用说了,第3章建表SQL里的FOREIGN KEY在MyISAM下根本不会生效。
| 配置项 | 推荐值 | 理由 |
|---|---|---|
| ENGINE | InnoDB | 支持事务、外键、行级锁 |
| CHARSET | utf8mb4 | 完整支持中文和特殊字符 |
| COLLATE | utf8mb4_unicode_ci | 通用排序规则,避免大小写问题 |
| 隔离级别 | REPEATABLE READ(默认) | 课程设计规模不需要调整 |
事务隔离级别保持MySQL默认的REPEATABLE READ就行,这个系统没有复杂的高并发场景,刻意改成READ COMMITTED属于过度优化。建表时把COMMENT写全,每个字段的中文说明都带上,这张表的注释本身就是数据字典的补充,老师翻表结构时体验会好很多,这属于成本极低但收益明显的细节。
4.3 用EXPLAIN确认索引真正生效
索引建了不等于一定会被用上。写完SQL以后,养成一个习惯:用EXPLAIN看一下执行计划。拿按系别统计的功能举例:
EXPLAIN SELECT s.department, COUNT(DISTINCT pe.student_id) AS exam_count FROM physical_exam pe JOIN student s ON pe.student_id = s.student_id WHERE pe.exam_date BETWEEN '2023-01-01' AND '2023-12-31' GROUP BY s.department;看结果里的type列和key列。type出现ref或range说明用上了索引,出现ALL就是全表扫描,要警惕。key列应该显示idx_exam_student_date或者主键,如果key是NULL,说明这个查询没有走到任何索引。Extra列如果出现Using filesort,说明排序没有走索引,需要考虑排序字段是否在联合索引里。
一个常见翻车点是给索引列套函数。比如把条件写成WHERE YEAR(exam_date) = 2023,索引就会失效,因为MySQL要先对每一行的exam_date做YEAR计算,才能判断是否等于2023,这跟全表扫描没区别。改成exam_date >= '2023-01-01' AND exam_date <= '2023-12-31',才能让范围条件命中索引。这个细节在文档里写一句,老师一眼就能看出你做没做过性能验证。
5. 避坑清单:课程设计里最容易翻车的五个现场
这份文档本身是一套完整流程,但我拆过不少类似项目,也见过很多照着做翻车的案例。下面五个坑是数据库课程设计里出现频率最高的,每一条都是真实发生过的场景。
5.1 表结构设计:主键选型和字段冗余的两个经典坑
第一个坑:学号用INT自增。
现象:往表里插学号为“20230001”的数据,插入后变成了20230001,前导零丢了;或者学号是字母数字混合(比如留学生编号W2023001),直接插入失败报错。
原因:把学号当成了数值型主键。自增INT只适合无业务含义的代理主键,学号是自然键,有固定格式,不该交给自增控制。
解决:学号统一用VARCHAR(20),已经建错的用ALTER改回来:
ALTER TABLE student MODIFY student_id VARCHAR(20) NOT NULL COMMENT '学号';改完后原来依赖INT类型的外键也要同步改类型,两条子表的外键列都得跟上,否则外键约束会报类型不一致。
第二个坑:把姓名、性别、系别直接塞进体检表。
现象:同一个学生体检了三年,体检表里躺着三份完全相同的姓名和系别数据。后来学生转系,要更新三条记录,漏掉一条就造成数据不一致。
原因:直接照搬需求分析里的原始字段清单,跳过了规范化步骤。
解决:把学生信息抽到student主表,体检表只保留学号外键。这也正好呼应第3.3节的内容。如果项目文档里必须保留原始字段,可以在数据字典里用“规范化后映射到学生表”做说明,不要直接让物理表跟着原始字段走。
5.2 查询与统计:索引失效和聚合口径不一致的两个坑
第三个坑:条件里给索引列套了函数,索引失效。
现象:查询同样一条数据,直接在WHERE里写等于条件只要几毫秒,换成WHERE YEAR(exam_date) = 2023之后慢了几十倍。EXPLAIN里type从ref变成ALL。
原因:对索引列使用函数后,MySQL无法直接通过B+树定位,只能逐行计算再过滤。
解决:把函数包住的范围改成直接的范围比较。这也是第4.3节强调过的习惯:写完SQL先EXPLAIN,看到ALL就停下来改写法。
第四个坑:聚合统计的口径没定好,AVG结果跟手工对不上。
现象:统计全体学生的平均身高,SQL跑出来的结果比手工按人数算出来的小,或者干脆不匹配。
原因:AVG函数会自动忽略NULL值。如果部分体检记录的身高字段为空,AVG是拿非空记录的总和除以非空记录的数量;手工算的时候可能用了总人数做分母,两边口径不同,结果当然对不上。
解决:统计前先明确口径。只统计有体检记录的人,保持默认AVG即可;如果要把缺检的人按0纳入统计,就要用AVG(COALESCE(height, 0))。并且要在文档的统计模块里写明这条口径,避免答辩时被追问“为什么AVG和实际感知不一样”。这里没有绝对的对错,但口径必须自洽。
5.3 文档一致性:数据流图、数据字典与建表SQL对不上的坑
第五个坑:数据字典里的字段在建表SQL里找不到。
现象:答辩时老师指着数据字典问“为什么数据流图里有的‘联系方式’,建表语句里没有?”学生当场翻车,不得不现场解释。
原因:画DFD和写数据字典在前,建表在后,后来改了表结构,没有回头更新前面的图和数据字典。文档变成三份对不上的独立文件,这是课程设计报告最致命的问题。
解决:把建表SQL当作唯一事实来源,表结构定稿后,倒回去逐字段核对数据字典,再核对DFD里的字段列表是否一致。我自己的习惯是每次改完表结构,就在文档里做一次全局搜索替换检查:搜数据字典里每个字段名,确认它在DFD和建表SQL里都有出处。字段映射保持一致,答辩时才能经得起追问。另外注意数据流图里的加工编号、数据存储名称也要和正文描述一致,像“体检文件”和“体检记录表”这种叫法差异,虽然不影响理解,但老师较真时也容易被挑出来。
6. 统计功能落地:一般统计与动态分析的SQL写法和样本验证
统计功能在文档里被明确分成一般统计和动态分析两类,这是整个系统里技术含量最高的模块,也最适合在答辩时展示。一般统计用聚合函数就能搞定,动态分析需要计算跨年度的增长量,得靠窗口函数或自连接。
6.1 一般统计:计数与平均值的SQL写法
按系别统计体检人数、平均身高、平均体重、平均年龄,直接GROUP BY系别:
SELECT s.department AS 系别, COUNT(DISTINCT pe.student_id) AS 体检人数, ROUND(AVG(pe.height), 2) AS 平均身高, ROUND(AVG(pe.weight), 2) AS 平均体重, ROUND(AVG(pe.age), 1) AS 平均年龄 FROM physical_exam pe LEFT JOIN student s ON pe.student_id = s.student_id GROUP BY s.department ORDER BY 体检人数 DESC;这里最值得注意的就是COUNT(DISTINCT pe.student_id)。同一个学生可能体检多次,如果写成COUNT(*),统计出来的“体检人数”实际是“体检人次”。文档里的一般统计要求是计数,计数按人头算还是按记录算,必须在文档里写明。ROUND保留小数位是为了让结果展示更干净,平均年龄保留一位小数就够。
6.2 动态分析:平均年增长值与年增长率的计算
动态分析要求“由健康历史求出平均年增长值和年增长率”。MySQL 8.0及以上可以用LAG窗口函数,不需要复杂的自连接:
WITH yearly AS ( SELECT student_id, YEAR(exam_date) AS exam_year, AVG(height) AS avg_height FROM physical_exam GROUP BY student_id, YEAR(exam_date) ) SELECT student_id, exam_year, avg_height, LAG(avg_height) OVER (PARTITION BY student_id ORDER BY exam_year) AS prev_avg_height, ROUND( (avg_height - LAG(avg_height) OVER (PARTITION BY student_id ORDER BY exam_year)) / LAG(avg_height) OVER (PARTITION BY student_id ORDER BY exam_year) * 100, 2 ) AS growth_rate_percent FROM yearly ORDER BY student_id, exam_year;PARTITION BY student_id表示按学生分组,每个学生单独计算;ORDER BY exam_year保证年份有序。LAG(avg_height)取当前行的上一年身高数据,两者相减就是年增长值,再除以上一年值乘100就是年增长率。第一年没有上一年数据,LAG返回NULL,这正好说明“该学生没有历史基线”。如果你的学校环境是MySQL 5.7,不支持窗口函数,就把yearly抽成临时视图,用自连接加a.exam_year = b.exam_year + 1来取上一年数据,思路一样,SQL稍长一些。
6.3 小额样本验证:用手工核对确认SQL可信
动态分析SQL写完一定要验证。课程设计的常见做法是造三条能心算验证的数据:学生S001,2022年身高165.00,2023年身高170.00。年增长值等于5.00,年增长率等于3.03%。拿这个预期结果跟SQL跑出来的输出比对,确认窗口函数的取值方向没问题再做进系统。
| 学号 | 年度 | 平均身高 | 上年身高 | 年增长值 | 年增长率 |
|---|---|---|---|---|---|
| S001 | 2023 | 170.00 | 165.00 | 5.00 | 3.03% |
| S002 | 2023 | 175.00 | 172.00 | 3.00 | 1.74% |
| S003 | 2023 | 160.00 | 158.00 | 2.00 | 1.27% |
从那以后我每次做类似的课程设计,都会先插入这三条样本数据,跑完SQL用计算器手工核对一遍,再开始写文档里的统计模块。这份文档整体是一套完整的可复用模板,你把建表SQL在本地MySQL跑通,再回填数据字典,就能快速改成自己学校的格式。希望帮到你。
本文还有配套的精品资源,点击获取