在线考试系统的成绩统计需要覆盖考生信息、试卷信息、题目信息、答题结果、最终得分等多个维度,MySQL表结构设计需要平衡数据存储效率和查询性能,避免后续统计时出现复杂的多表关联或者数据缺失问题。

核心业务场景分析
在设计表结构前,先明确成绩统计的核心需求:
- 统计单个考生的所有考试得分、排名、错题分布
- 统计某场考试的整体平均分、最高分、最低分、分数段分布
- 统计某道题的正确率、错误选项分布
- 支持按时间、考试类型、考生群体等维度筛选统计数据
核心表结构设计
1. 用户表(sys_user)
存储考生基础信息,成绩统计时关联考生身份使用。
| 字段名 | 类型 | 说明 |
|---|---|---|
| user_id | bigint(20) unsigned | 主键,用户ID |
| username | varchar(50) | 用户名 |
| real_name | varchar(30) | 真实姓名 |
| class_id | bigint(20) unsigned | 所属班级ID,用于按群体统计成绩 |
| create_time | datetime | 创建时间 |
2. 试卷表(exam_paper)
存储试卷基础信息,关联考试场次和题目集合。
| 字段名 | 类型 | 说明 |
|---|---|---|
| paper_id | bigint(20) unsigned | 主键,试卷ID |
| paper_name | varchar(100) | 试卷名称 |
| total_score | int(11) | 试卷总分 |
| exam_type | tinyint(4) | 考试类型 1-正式考试 2-模拟考试 |
| start_time | datetime | 考试开始时间 |
| end_time | datetime | 考试结束时间 |
3. 题目表(exam_question)
存储题目基础信息,统计题目正确率时使用。
| 字段名 | 类型 | 说明 |
|---|---|---|
| question_id | bigint(20) unsigned | 主键,题目ID |
| question_type | tinyint(4) | 题目类型 1-单选 2-多选 3-判断 4-简答 |
| content | text | 题目内容 |
| score | int(11) | 题目分值 |
| answer | varchar(500) | 正确答案 |
4. 试卷题目关联表(paper_question_rel)
存储试卷和题目的对应关系,支持同一题目出现在不同试卷中。
| 字段名 | 类型 | 说明 |
|---|---|---|
| rel_id | bigint(20) unsigned | 主键,关联ID |
| paper_id | bigint(20) unsigned | 试卷ID |
| question_id | bigint(20) unsigned | 题目ID |
| sort | int(11) | 题目在试卷中的排序 |
5. 考试记录表(exam_record)
存储考生参与考试的整体记录,包含最终得分等核心统计字段。
| 字段名 | 类型 | 说明 |
|---|---|---|
| record_id | bigint(20) unsigned | 主键,考试记录ID |
| user_id | bigint(20) unsigned | 考生ID |
| paper_id | bigint(20) unsigned | 试卷ID |
| total_score | int(11) | 最终得分 |
| submit_time | datetime | 提交时间 |
| status | tinyint(4) | 状态 1-已提交 2-已批改 |
6. 答题详情表(exam_answer_detail)
存储每道题的答题结果,用于统计错题分布、题目正确率等细粒度数据。
| 字段名 | 类型 | 说明 |
|---|---|---|
| detail_id | bigint(20) unsigned | 主键,详情ID |
| record_id | bigint(20) unsigned | 考试记录ID |
| question_id | bigint(20) unsigned | 题目ID |
| user_answer | varchar(500) | 考生答案 |
| is_correct | tinyint(1) | 是否正确 1-正确 0-错误 |
| get_score | int(11) | 该题得分 |
统计查询示例
1. 统计某场考试的整体成绩
查询指定试卷的平均分、最高分、最低分、参考人数:
SELECT
p.paper_name,
COUNT(r.record_id) AS exam_count,
AVG(r.total_score) AS avg_score,
MAX(r.total_score) AS max_score,
MIN(r.total_score) AS min_score
FROM exam_record r
LEFT JOIN exam_paper p ON r.paper_id = p.paper_id
WHERE r.paper_id = 1
GROUP BY p.paper_id, p.paper_name;
2. 统计某道题的正确率
查询指定题目在所有考试中的正确率:
SELECT
q.content AS question_content,
COUNT(d.detail_id) AS total_answer_count,
SUM(d.is_correct) AS correct_count,
CONCAT(ROUND(SUM(d.is_correct) / COUNT(d.detail_id) * 100, 2), '%') AS correct_rate
FROM exam_answer_detail d
LEFT JOIN exam_question q ON d.question_id = q.question_id
WHERE d.question_id = 10
GROUP BY q.question_id, q.content;
3. 查询考生成绩排名
查询某场考试中所有考生的得分排名:
SELECT
u.real_name,
r.total_score,
@rank := @rank + 1 AS rank_num
FROM exam_record r
LEFT JOIN sys_user u ON r.user_id = u.user_id,
(SELECT @rank := 0) t
WHERE r.paper_id = 1
ORDER BY r.total_score DESC;
设计注意事项
首先,所有表的主键建议使用自增bigint类型,保证唯一性和查询性能。其次,频繁用于统计查询的字段如paper_id、user_id、question_id需要建立普通索引,提升关联查询速度。另外,答题详情表中is_correct字段可以提前计算存储,避免统计时重复判断答案正确性,减少计算开销。最后,如果考试数据量较大,可以对exam_record表按时间进行分表,保证历史成绩统计的效率。