简介这是一套基于PHP与MySQL的学生成绩查询系统毕业设计资源面向计算机相关专业毕业生及PHP/MySQL初学者覆盖学生信息管理、成绩录入与查询、课程设置、用户登录及权限控制等典型模块可作为课程设计或毕业论文项目的完整参考。资源包共66个文件压缩后约12.3MB包含22个PHP源码文件、htm/css前端页面、SQL数据库脚本、doc/txt说明文档、两个EXE操作录屏管理员模块、学生模块与数据库设计另含项目说明与录屏备份的RAR资料包便于按模块对照学习。目前已有307人浏览学习适合希望快速搭建成绩管理系统或借鉴完整毕设流程的读者。资源中的源码与数据库脚本可直接部署验证录屏讲解能帮助梳理系统分层与核心业务逻辑项目摘要文档和说明文件则为论文撰写与二次开发提供了辅助资料整体学习路径覆盖从设计到实现的各个关键环节。1. 学生成绩查询系统一个三天能跑通、五天能交差的 PHPMySQL 毕设源码A同学接到任务时有点慌课程设计要做一个学生成绩管理系统数据库课只教过 SELECT前端只会写表格还要求登录、分角色、成绩统计全都要有。他翻到一个 phpmysql 的毕设源码包整个项目没有框架依赖管理员、教师、学生三种角色各管各的功能从数据库脚本到前端页面一次给齐。这类源码最适合两类人一类是正在做 PHPMySQL 毕业设计或课程设计的学生需要能讲清楚登录鉴权、关联查询、权限隔离这些答辩必问点另一类是刚学完 PHP 语法、想看看一个完整项目怎么组织文件的新手。系统核心就是一张成绩表关联学生、课程、教师三张基础表数据链路短、逻辑直白拿来改改字段和页面就能变成自己的选题。2. 技术选型与整体架构原生 PHP MySQL不玩框架但五脏俱全2.1 为什么毕设项目里 PHPMySQL 是常青树拿到源码先看技术栈。这套系统用原生 PHP 5.6/7.x 编写数据库是 MySQL 5.7 或 8.0前端就是普通的 HTML CSS JavaScript没有引入 Laravel、ThinkPHP 这类重量级框架。有人会觉得“不用框架是不是太原始”但放在毕业设计场景里原生 PHP 恰恰是优势数据库连接、会话管理、SQL 执行全部显式写在代码里答辩时老师问“你这行是干什么的”你能从 require 一直讲到 fetch_assoc不需要绕一层框架的 Route 和 ORM。从运行角度看它需要一个 Apache 或 Nginx 能解析 PHP 的环境数据库单独跑在 MySQL 服务上。本地开发我一般直接装集成环境常见的 phpStudy 或 XAMPP 一类把源码丢到 web 根目录导入 SQL 脚本就能访问。这个套路本身也是 PHP 项目最经典的黑匣子破解方式——不用关心服务怎么编译只要 PHP 扩展开启、端口不冲突项目就能转起来。2.2 目录结构三种角色各占一个子目录源码的目录组织非常清晰基本按“Admin / Teacher / Student”三个端拆开公共部分单独抽出来。一个典型的目录骨架长这样score-system/ ├── index.php // 入口按登录角色跳转 ├── admin/ // 管理员端 │ ├── login.php // 管理员登录页 │ ├── dashboard.php // 统计仪表盘 │ ├── student.php // 学生账号管理 │ ├── teacher.php // 教师账号管理 │ ├── course.php // 课程管理 │ └── score.php // 全量成绩查看/导出 ├── teacher/ // 教师端 │ ├── login.php │ ├── score_input.php // 录入成绩 │ └── my_course.php // 查看任课班级 ├── student/ // 学生端 │ ├── login.php │ └── my_score.php // 查询个人成绩 ├── common/ │ ├── config.php // 数据库连接配置 │ ├── functions.php // 公共函数如字段过滤 │ └── auth.php // 登录态检查每个页面顶部引用 ├── sql/ │ └── init.sql // 建库建表 初始账号数据 └── assets/ ├── css/ └── js/这个结构的优点是“页面即模块”每新增一个功能就是往对应角色目录里加一个 PHP 文件再在导航菜单里加一行链接。写毕设文档时功能模块图也能直接照这个目录画不用额外编。缺点也有——没有统一路由所有页面靠文件直出但小系统这样做反而最不容易出错。2.3 请求生命周期一个成绩查询从点击到渲染都经历了什么我习惯用一个具体操作把整个项目串起来讲。假设学生登录后点击“我的成绩”这条链路是这样的浏览器把登录表单 POST 到 student/login.phplogin.php 顶部先 session_start()然后引入 common/config.php 拿到数据库连接对象。接着用预处理方式查 student 表把用户输入的密码做 md5 后比对匹配成功就把 student_id 写进 $_SESSION然后 header 跳转到 my_score.php。my_score.php 第一行不是业务代码而是 require auth.php——auth.php 里检查 $_SESSION 里有没有指定字段没有就直接 redirect 回登录页这一步是整个系统的权限闸门。通过鉴权后my_score.php 执行一条 JOIN 查询把 score 表、course 表按课程 ID 关联起来拿当前登录学生的全部成绩记录最后在循环里拼 HTML 表格输出。整个流程没有 AJAX、没有前后端分离每次点击都是一次完整的“页面刷新式”请求逻辑非常符合刚学完 PHP 的认知水平。2.4 本地跑起来四步完成环境搭建本地运行我推荐用集成环境省去自己编译 PHP 的时间。步骤按这个顺序走第一步安装集成环境并启动 Apache 和 MySQL 服务检查端口是否被占用如果 80 或 3306 被占用在配置里改掉。第二步把源码包解压到 Web 根目录比如 phpStudy 的 WWW 目录下确保 index.php 能通过 http://localhost/score-system/index.php 访问。第三步用 phpMyAdmin 或命令行执行 sql/init.sql这一步会创建数据库、六张表以及默认管理员账号。第四步修改 common/config.php 里的数据库密码改成你本地 MySQL 的实际密码。?php // common/config.php define(DB_HOST, 127.0.0.1); define(DB_USER, root); define(DB_PASS, root); // 改成你本地 MySQL 密码 define(DB_NAME, student_score_db); $mysqli new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); if ($mysqli-connect_errno) { die(数据库连接失败: . $mysqli-connect_error); } $mysqli-set_charset(utf8mb4);config.php 是整个项目的命门页面加载失败八成是这个文件的地址或密码不对。设置字符集这行千万别省不然后面中文乱码会一直缠着你。跑通之后默认管理员账号通常是 admin / admin123登录后第一件事是去密码修改功能里换掉默认密码这一点在答辩演示时容易被评委抓。3. 数据库设计六张表如何撑起三类角色的全部业务3.1 核心表结构学生、课程、成绩三张表是绝对主角打开 sql/init.sql库名一般是 student_score_db六张表分别是管理员表、学生表、教师表、班级表、课程表和成绩表。其中成绩表是整个系统的核心其他表都是为它服务的。学生表保存学号、姓名、密码和班级 ID教师表保存工号、姓名和所属教研室班级表只做简单关联避免在学生表里重复存储班级名称。学生表的结构很典型字段不多但每一列都在业务里用得到字段类型说明idint 自增主键student_novarchar(20) 唯一学号登录账号namevarchar(50)姓名passwordvarchar(64)md5 加密后的密码class_idint关联班级表phonevarchar(20)选填联系电话成绩表则承担了考试成绩的记录、查询和统计三个职责字段设计如下字段类型说明idint 自增主键student_idint学生 IDcourse_idint课程 IDteacher_idint任课教师 IDscoredecimal(5,2)成绩支持一位小数semestervarchar(20)学期如 2024-2025-1exam_datedate考试日期remarkvarchar(200)补考、缓考等备注score 字段用 decimal(5,2) 而不是 float成绩这种精确数据不允许出现浮点误差这也是答辩老师爱问的一个细节。3.2 表间关系与外键策略成绩表为什么要设联合唯一索引六张表的关联关系不复杂班级表是一级学生表通过 class_id 挂在班级下课程表独立维护成绩表通过 student_id、course_id、teacher_id 三个外键分别关联学生、课程和教师。一个学生有多个成绩一个课程被多个学生选修所以成绩表本质上是一张多对多的中间表额外挂了一个教师字段记录是谁教的。设计上有一个值得注意的细节成绩表加了一个针对 student_id、course_id、semester 三个字段的联合唯一索引。这个索引有双重作用第一是查询加速学生查成绩时 WHERE 条件正好命中这个索引第二是防止同一个学生、同一门课、同一学期被重复录入两次数据层面就把脏数据堵死了。实际写 init.sql 时建议给外键字段都加上普通索引否则联表 JOIN 的数据量一大查询会明显变慢。3.3 初始化脚本建表语句里藏着三个容易忽略的点init.sql 里除了 CREATE TABLE还负责往三张用户表里插入默认账号。学生默认密码一般是学号本身教师默认密码是工号管理员是 admin123这些初始数据用于首次登录测试。一个合格的教学系统必须让学生能第一次登录就进入系统否则演示时就卡在登录页了。建表语句里有三个坑值得留意。第一是所有表都要显式指定 DEFAULT CHARSETutf8mb4不能沿用 MySQL 默认的 latin1否则中文姓名直接变问号。第二是字符串类型长度要克制学号 20 位、姓名 50 位足够不要随手写 varchar(255)成绩表和班级表这么做会让索引体积变大。第三是 ENGINE 要指定 InnoDBMyISAM 不支持事务成绩录入时如果中途出错会出现部分学生有成绩、部分没有的尴尬情况。CREATE TABLE score ( id int NOT NULL AUTO_INCREMENT, student_id int NOT NULL, course_id int NOT NULL, teacher_id int NOT NULL, score decimal(5,2) NOT NULL, semester varchar(20) NOT NULL, exam_date date DEFAULT NULL, remark varchar(200) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_stu_course_semester (student_id, course_id, semester), KEY idx_course_id (course_id), KEY idx_teacher_id (teacher_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;成绩表创建脚本里我保留了三个索引联合唯一索引防重复录分course_id 和 teacher_id 的单列索引支持按课程和按教师筛选。如果后续要加“按班级统计平均分”的功能建议再加一个 class_id 冗余到成绩表或者在查询时 JOIN 学生表这属于典型的空间换时间取舍。4. 核心代码拆解登录鉴权、联表查询与成绩录入的完整实现4.1 登录与权限隔离$_SESSION 三行代码挡住未登录用户登录是第一个要拆解的核心点。系统没有把登录逻辑写在某个框架的中间件里而是直接放在 login.php 和 auth.php 两个文件里逻辑非常直观。登录接收 role 参数决定去查哪张用户表密码统一 md5 处理查出来后把用户 ID 写进 SESSION。?php // student/login.php 处理部分 session_start(); require_once __DIR__ . /../common/config.php; $role $_POST[role] ?? student; $account trim($_POST[account] ?? ); $password md5(trim($_POST[password] ?? )); // 角色与表名的映射必须是白名单不能直接把 role 拼进 SQL $tableMap [student student, teacher teacher, admin admin]; $table $tableMap[$role] ?? student; // 用预处理方式查用户 $stmt $mysqli-prepare(SELECT id, name FROM {$table} WHERE student_no ? AND password ?); $stmt-bind_param(ss, $account, $password); $stmt-execute(); $user $result_test $stmt-get_result()-fetch_assoc(); if ($user) { $_SESSION[user_id] $user[id]; $_SESSION[real_name] $user[name]; $_SESSION[role] $table; header(Location: . $table . /index.php); // 按角色跳转 exit; } else { echo scriptalert(账号或密码错误);history.back();/script; }这段代码里有几个处理值得借鉴。表名用了白名单映射而不是直接把 POST 的 role 拼进 SQL杜绝了表名注入密码比对统一走 md5 后再进 SQL数据库里不会存明文登录成功后立刻 exit防止 header 后续被其他输出干扰。参数说明role 对应三种角色account 是学号或工号password 是前端表单传来的明文进入后端先 md5 再比对。auth.php 则是每个受保护页面顶部的“门禁”代码只有几行但作用关键。它会检查 SESSION 里有没有 user_id没有就跳回登录页同时按角色判断访问权限——比如 admin 目录下的页面要求 role 必须是 admin否则直接拦下。这个文件的引用方式是在每个 PHP 页面开头写 require_once属于手动实现的“路由守卫”。4.2 学生成绩查询一条 JOIN 把三张表串成成绩单学生端最核心的就是 my_score.php 里的查询。按照需求学生想看的是“我选了哪些课、各考了多少分、学分多少”这些字段分散在 score 表和 course 表里必须通过 JOIN 合并。SELECT c.course_name, c.credit, sc.score, sc.semester, sc.exam_date FROM score sc JOIN course c ON sc.course_id c.id WHERE sc.student_id ? ORDER BY sc.semester DESC, c.id ASC;JOIN 的思考过程是这样的成绩表里有 course_id 但没课程名课程表里有课程名和学分但没成绩所以以成绩表为主表LEFT JOIN 课程表后每一行成绩都能带上课程名和学分。WHERE 条件里的 student_id 来自 SESSION不用学号直接查避免用户在 URL 里改参数看别人的成绩。ORDER BY 先按学期倒序再按课程 ID 排序确保最新学期的课排在最前面。在 PHP 里执行这条查询后用常规的 while 循环遍历结果集拼装成表格行。这里有一个常见误区有人会把成绩表和学生表 JOIN 上再取 student_no 当展示字段其实学生姓名在 SESSION 里已经拿到了一行代码就能显示在页头没必要多 JOIN 一张表增加查询负担。如果按学期做筛选只需要在 WHERE 后面加一个 semester ? 条件参数同样用预处理绑定就能做成下拉框筛选项。4.3 成绩录入预处理循环 联合索引防重复教师端录入成绩是最容易出错的模块。它的难点不在 SQL 本身而在批量逻辑一个班级几十个学生每行一个分数提交时要循环写入同时还得保证同一个学生同一门课不会被录两次。?php // teacher/score_input.php 核心循环 $course_id (int)$_POST[course_id]; $semester trim($_POST[semester]); $scoreList $_POST[score_list]; // 结构: [student_no score] // 预取所有学号对应的学生 ID避免循环里一条一条查库 $noToId []; $res $mysqli-query(SELECT id, student_no FROM student WHERE class_id . (int)$_POST[class_id]); while ($row $res-fetch_assoc()) { $noToId[$row[student_no]] $row[id]; } $stmt $mysqli-prepare( INSERT INTO score (student_id, course_id, semester, score) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE score VALUES(score) ); foreach ($scoreList as $studentNo $score) { if (!isset($noToId[$studentNo])) continue; // 学号不存在则跳过 $sid $noToId[$studentNo]; $scoreVal (float)$score; if ($scoreVal 0 || $scoreVal 100) continue; // 范围校验 $stmt-bind_param(iisd, $sid, $course_id, $semester, $scoreVal); $stmt-execute(); }这段代码的核心技巧是先用一次查询把整个班的学号到 ID 映射拉进数组循环时直接查数组而不是每录一个学生就执行一次 SELECT。这个优化在大批量录入时效果非常明显。ON DUPLICATE KEY UPDATE 配合第 3 章说的联合唯一索引实现了“有记录就更新分数没记录就插入”的幂等逻辑教师重复提交表单不会产生重复数据。bind_param 的四个参数类型 iisd 分别对应用户 ID整数、课程 ID整数、学期字符串、分数小数。录入完成后通常会做一个统计展示SQL 用聚合函数一组出结果给教师看这个班这节课的及格情况SELECT COUNT(*) AS total, ROUND(AVG(score), 1) AS avg_score, SUM(CASE WHEN score 60 THEN 1 ELSE 0 END) AS passed, ROUND(SUM(CASE WHEN score 60 THEN 1 ELSE 0 END) / COUNT(*) * 100, 1) AS pass_rate FROM score WHERE course_id ? AND semester ?;这条统计 SQL 在毕设答辩中属于“亮点型”代码COUNT 统计总人数AVG 算平均分SUM(CASE WHEN) 结构在不增加子查询的情况下统计及格人数再除以总数得到及格率。如果老师追问排名怎么写可以顺势引出 MySQL 8.0 的窗口函数 RANK()这一步就能展示你在数据库知识上的深度。5. 部署避坑指南PHP 环境、连接、乱码与权限的五个血泪问题5.1 打开页面直接报错Call to undefined function mysqli_connect现象访问任何页面都出现类似 Fatal error: Uncaught Error: Call to undefined function mysqli_connect() 的报错页面完全白屏。原因PHP 安装时没有开启 mysqli 扩展或者 php.ini 里对应的 extension 行被注释掉了。这种现象在集成环境里不常见但如果你用的是系统自带 PHP 或者手工编译的 PHP非常容易遇到。解决打开 PHP 安装目录下的 php.ini搜索 mysqli把 ;extensionmysqli 这一行前面的分号删掉保存后重启 Apache 或 PHP-FPM。如果是集成环境直接在面板里勾选 PHP 扩展即可。验证方式是在 web 目录放一个临时文件写入 phpinfo()搜索 mysqli 字段出现才说明扩展生效。提示PHP 7.4 以后mysqli 扩展已不再默认编译进核心很多精简版 PHP 包需要单独启用。5.2 数据库连接失败Access denied for user密码明明是对的现象config.php 里填的密码和 MySQL 一致但页面提示 Access denied for user rootlocalhost。原因MySQL 8.0 默认使用 caching_sha2_password 认证插件而较老版本的 PHP mysqli 扩展只支持 mysql_native_password。两者握手失败MySQL 直接拒绝连接。解决登录 MySQL 命令行执行下面的 SQL 把 root 用户切回旧认证方式ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;另一个容易翻车的点是端口MySQL 如果改过端口比如 3307config.php 里也要同步改mysqli 构造函数第五个参数传端口号。我见过有人改了数据库端口却忘了改配置排查了半小时才发现是端口对不上。5.3 登录成功却跳转失败Cannot modify header information现象登录时提示 Warning: Cannot modify header information - headers already sent账号密码正确但页面不跳转或者跳转后停留在空白页。原因PHP 在输出任何内容之后就不能再发送 header。最常见的原因是 PHP 文件开头有隐藏的 BOM 字符或者 require 的文件末尾多了一个空格或换行导致 session_start() 或 header() 执行前已经有内容输出。解决用编辑器将项目下所有 PHP 文件统一转为 UTF-8 无 BOM 编码。在 VS Code 里右下角点击编码选择“通过编码保存”选 UTF-8 保存即可。另一个习惯是写代码时不写闭合的 ? 标签这样能彻底避免文件末尾的空行隐患。这个坑在 Windows 自带的记事本编辑过源码后特别容易出现因为是记事本保存时默认加 BOM。5.4 页面全是问号数据库和页面双重乱码现象学生姓名和课程名显示为??或者页面正常但数据库里存的是乱码。原因乱码可能出现在三个环节数据库表字符集不是 utf8mb4、PHP 连接没设置字符集、页面本身的 meta 声明缺失。任何一个环节断裂中文就会断裂。解决按三层逐一排查。第一层页面 HTML 的 head 里必须有meta charsetUTF-8第二层config.php 里必须调 set_charset(utf8mb4)第三层建表语句的 DEFAULT CHARSET 必须是 utf8mb4。如果数据库已经建成且存了乱码先改表字符集再重建数据改完字符集后旧数据可能仍是乱码此时只能重新录入所以我习惯在跑通业务前先把字符集确认好。注意utf8 在 MySQL 里是 utf8mb3 的别名不支持四字节字符统一用 utf8mb4 最稳妥。5.5 成绩录不进去表单提交后没反应现象教师在录入页面填完分数点提交页面刷新后成绩表里还是空的也没有任何报错信息。原因这种情况最隐蔽通常不是 SQL 语法错而是业务逻辑拦截了写入。可能的原因有三类第一score_list 的字段名和后端 $_POST 取的名字不一致循环里一个都没匹配到第二第 4.3 节的代码里做了范围校验分数小于 0 或大于 100 被 continue 跳过第三学号与预取映射对不上isset($noToId[$studentNo]) 为 false全部被过滤掉。解决先在 score_input.php 末尾临时加一行输出打印 $_POST 结构和循环处理计数确认表单数据是否传到后端。接着把范围校验临时注释掉看是否能写入。如果还是不行在 MySQL 里开启通用查询日志查看实际执行的 SQL 语句问题通常立刻暴露——SQL 里如果是空参数就在参数获取环节找问题如果参数正常但没写入再查联合唯一索引是不是误伤。排查完记得把调试代码删掉。6. 二次开发加字段、做排名、导出 CSV 的三个改造方案6.1 从“一考定成绩”改成“平时分 期末分”综合评定很多教学场景不只看期末一考还有平时分、作业分、期末分按比例合成总评。这个改动在系统里属于“加字段 改录入”两步走。先在成绩表追加字段同时把历史数据迁移一下ALTER TABLE score ADD usual_score DECIMAL(5,2) DEFAULT 0, ADD final_score DECIMAL(5,2) DEFAULT 0, ADD total_score DECIMAL(5,2) DEFAULT 0; UPDATE score SET total_score ROUND(usual_score * 0.3 final_score * 0.7, 2);然后把录入页面 score_input.php 里的表单改成三个输入框提交时同时接收 usual_score 和 final_score后端按比例算 total_score。注意 total_score 的计算不能放 SQL 里用生成列因为生成列无法用 ON DUPLICATE KEY UPDATE 直接更新这个坑我踩过老老实实后端算好再写入最稳。6.2 班级排名MySQL 8.0 窗口函数与 5.7 变量法学生端如果能显示“我这门课在班里排第几”整个系统的完整度会明显提升。MySQL 8.0 直接用窗口函数一行解决SELECT student_id, semester, score, RANK() OVER (PARTITION BY course_id, semester ORDER BY score DESC) AS course_rank FROM score WHERE course_id ? AND semester ?;RANK() 按课程和学期分组组内按分数倒序排名同分数并列。MySQL 5.7 不支持窗口函数就得用会话变量的方式模拟写法稍绕SET prev_course : , rank : 0; SELECT student_id, score, course_rank FROM ( SELECT student_id, score, rank : IF(prev_course CONCAT(course_id, semester), rank 1, 1) AS course_rank, prev_course : CONCAT(course_id, semester) AS cur_group FROM score ORDER BY course_id, semester, score DESC ) t;变量法的思路是按课程和学期分组每组第一个学生的排名重置为 1后面每行加 1。要注意 ORDER BY 必须在子查询里先完成否则分组错乱排名全废。6.3 导出成绩单CSV 加 BOM 才能被 Excel 正常打开管理员和教师端都适合加一个“导出当前列表为 CSV”的按钮代码量不大但体验提升明显。关键坑是在输出 CSV 之前必须加 BOM 头否则 Excel 打开中文会乱码而 Windows 记事本打开却正常玄学味道十足。?php // admin/score.php 导出逻辑 header(Content-Type: text/csv; charsetUTF-8); header(Content-Disposition: attachment; filenamescore_export.csv); echo \xEF\xBB\xBF; // UTF-8 BOM让 Excel 正确识别编码 $fp fopen(php://output, w); fputcsv($fp, [学号, 姓名, 课程, 学期, 成绩]); while ($row $result-fetch_assoc()) { fputcsv($fp, [$row[student_no], $row[name], $row[course_name], $row[semester], $row[score]]); } fclose($fp); exit;这里用 php://output 流式输出不落盘、不占服务器空间适合成绩这种量级的数据。导出的文件还能作为答辩时“系统支持数据导出”的功能演示比截图更有说服力。在那之后我自己每次部署这类 PHPMySQL 源码都会强制走一遍固定清单先开 mysqli 扩展再确认数据库编码再检查文件 BOM然后测登录、试录入最后查一遍 SQL 日志。这套流程跑下来九成以上的环境问题五分钟内都能定位。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?