You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MariaDB 5.5中总分相同时如何通过MySQL查询实现正确递增排名

考试成绩连续排名实现方案

需求说明

考试成绩查询场景需要实现以下排名规则:

  • 总分相同的用户排名一致
  • 排名整体连续递增,不出现跳号(即同分之后的排名紧接前序名次,例如两个并列第1之后下一名为第2,而非第3)
  • 当前使用数据库版本为 MariaDB 5.5.68,该版本不支持DENSE_RANK()等窗口函数,此前尝试用自定义变量实现时逻辑错误,仅返回行号未实现同分同排名效果。

原有问题SQL

SELECT
    `users`.`id` AS userID,
    `user_tests`.`id`,
    `users`.`profilePic`,
    `users`.`firstName`,
    `user_tests`.`userId`,
    `user_tests`.`isFirstAttempt`,
    `user_tests`.`total_marks`,
    FIND_IN_SET(
        `user_tests`.`total_marks`,
        (
        SELECT
            GROUP_CONCAT(
                DISTINCT `user_tests`.`total_marks`
            ORDER BY
                CAST(
                    `user_tests`.`total_marks` AS DECIMAL(5, 3)
                )
            DESC
            )
        FROM
            `user_tests`
        WHERE
            `user_tests`.`testSeriesId` = '856' AND `user_tests`.`isFirstAttempt` = '1'
    )
    ) AS rank,
FROM
    `user_tests`
LEFT JOIN `users` ON `users`.id = `user_tests`.`userId`
WHERE
    `user_tests`.`isFirstAttempt` = '1' AND `user_tests`.`testSeriesId` = '856'
ORDER BY
    CAST(
        `user_tests`.`total_marks` AS DECIMAL(5, 3)
    )
DESC
    ,
    `submissionTimeInMinutes` ASC,
    `rank` ASC;

原有方案通过FIND_IN_SET匹配去重后的分数位置实现排名,存在两个问题:

  • 性能差:需要先拼接全量去重分数字符串,数据量大时执行效率极低
  • 不兼容次级排序规则:如果两个用户总分相同但提交用时不同,会被强行分配相同排名,无法匹配业务排序优先级

正确实现代码

基于MariaDB 5.5支持的自定义变量实现连续排名逻辑,写法如下:

SELECT
    sorted_res.userID,
    sorted_res.id,
    sorted_res.profilePic,
    sorted_res.firstName,
    sorted_res.userId,
    sorted_res.isFirstAttempt,
    sorted_res.total_marks,
    @cur_rank := IF(
        @last_score = CAST(sorted_res.total_marks AS DECIMAL(5,3)),
        @cur_rank,
        @cur_rank + 1
    ) AS rank,
    @last_score := CAST(sorted_res.total_marks AS DECIMAL(5,3))
FROM (
    -- 内层先按排序规则把所有结果排好序,避免变量计算顺序错乱
    SELECT
        `users`.`id` AS userID,
        `user_tests`.`id`,
        `users`.`profilePic`,
        `users`.`firstName`,
        `user_tests`.`userId`,
        `user_tests`.`isFirstAttempt`,
        `user_tests`.`total_marks`,
        `user_tests`.`submissionTimeInMinutes`
    FROM `user_tests`
    LEFT JOIN `users` ON `users`.id = `user_tests`.`userId`
    WHERE
        `user_tests`.`isFirstAttempt` = '1'
        AND `user_tests`.`testSeriesId` = '856'
    ORDER BY
        CAST(`user_tests`.`total_marks` AS DECIMAL(5,3)) DESC,
        `user_tests`.`submissionTimeInMinutes` ASC
) sorted_res, (SELECT @cur_rank := 0, @last_score := NULL) AS var_init
ORDER BY
    CAST(sorted_res.total_marks AS DECIMAL(5,3)) DESC,
    sorted_res.submissionTimeInMinutes ASC;

逻辑说明

  • 内层子查询先完成数据筛选和排序,严格按照「总分从高到低、用时从少到多」的规则输出结果,避免外层变量计算时因为数据顺序错误导致排名异常
  • 通过两个自定义变量实现排名计算:
    • @cur_rank存储当前计算到的排名值,初始化为0
    • @last_score存储上一行记录的总分,初始化为NULL
  • 逐行遍历排序后的结果时,判断当前行总分和上一行是否相等:相等则排名不增长,不相等则排名+1,天然实现同分同名次、排名连续无跳号的效果
  • 该写法性能远高于原有GROUP_CONCAT+FIND_IN_SET方案,支持任意多字段的次级排序规则

内容的提问来源于stack exchange,提问作者Sandeep Acharya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:06:19