如何使用Laravel实现学生总分排名(支持同分同名次)
Laravel 学生同分并列排名实现方案
需求规则:按学生总分从高到低排序,总分越高排名越靠前,总分相同的学生排名一致。
前置准备:模型关联
首先在Student模型中定义和成绩表的一对多关联,方便后续业务复用:
// app/Models/Student.php public function marks() { return $this->hasMany(Mark::class, 'id_student'); }
对应Mark模型中定义反向关联即可:
// app/Models/Mark.php public function student() { return $this->belongsTo(Student::class, 'id_student'); }
场景1:获取全量带排名的学生列表
核心逻辑:先聚合每个学生的总分并按分数降序排序,遍历集合时动态计算排名——分数和上一位相同则沿用相同排名,分数低于上一位则更新排名为当前遍历位置+1(索引从0开始)。
// 聚合所有学生总分,无成绩的学生总分记为0 $students = Student::select('students.id', 'students.name', 'students.class_id') ->selectRaw('COALESCE(SUM(marks.mark), 0) as total_score') ->leftJoin('marks', 'students.id', '=', 'marks.id_student') ->groupBy('students.id', 'students.name', 'students.class_id') ->orderByDesc('total_score') ->get(); // 处理并列排名 $currentRank = 1; $lastScore = null; $rankedList = $students->map(function ($student, $index) use (&$currentRank, &$lastScore) { if (!is_null($lastScore) && $student->total_score < $lastScore) { $currentRank = $index + 1; } $student->rank = $currentRank; $lastScore = $student->total_score; return $student; });
返回结果示例(对应给出的测试数据):
| id | name | total_score | rank |
|---|---|---|---|
| 4 | Mark | 120 | 1 |
| 2 | Frank | 115 | 2 |
| 3 | Bright | 90 | 3 |
| 1 | Emma | 0 | 4 |
如果后续出现两个学生同分,比如两个120分,两人rank都是1,下一位115分的学生rank直接为3,符合并列排名规则。
场景2:查询单个学生的排名
如果只需要查指定学生的排名,不需要拉取全量列表遍历,可以直接通过子查询计算,性能更高:
$targetStudentId = 2; // 传入要查询的学生ID // 先拿到学生总分 $student = Student::select('id', 'name') ->selectRaw('COALESCE(SUM(marks.mark), 0) as total_score') ->leftJoin('marks', 'id', '=', 'marks.id_student') ->groupBy('id', 'name') ->find($targetStudentId); if ($student) { // 排名 = 总分比当前学生高的人数 + 1 $student->rank = Student::leftJoin('marks', 'students.id', '=', 'marks.id_student') ->groupBy('students.id') ->havingRaw('SUM(marks.mark) > ?', [$student->total_score]) ->count() + 1; }
优化提示
- 如果需要按班级维度排名,只需要在上述所有查询中增加
where('class_id', $classId)条件即可,排名逻辑无需调整 - 当学生数据量超过10万级时,建议给
marks表的id_student字段添加索引,大幅提升聚合查询速度 - 排名计算逻辑如果需要多处复用,可以封装成自定义集合方法或者公共Helper函数,避免重复写代码
内容的提问来源于stack exchange,提问作者Bezara Florent
相关产品推荐
相关产品推荐

