MySql5.7排名

107 阅读3分钟

一、连续并列排名

需求:相同值记录共享同一排名,总排名连续(如 1,1,3)

1. 自连接实现

SELECT s1.id, s1.score,
       (SELECT COUNT(DISTINCT s2.score)
        FROM scores s2
        WHERE s2.score >= s1.score) AS rank
FROM scores s1
ORDER BY s1.score DESC;

解释

  • 使用 COUNT(DISTINCT s2.score) 去重相同分数
  • 结果示例:1,1,3

2. 变量实现

SELECT s.score,
IF(@pre_score = s.score, @cur_rank, @cur_rank := @cur_rank + 1) ranking,
@pre_score := s.score
FROM score s, (SELECT @cur_rank :=0, @pre_score := NULL) r
ORDER BY s.score DESC;

解释

  • 变量 @pre_score 记录上一条分数
  • @cur_rank 在分数变化时递增

二、连续不并列排名

需求:每条记录唯一排名,连续递增(如 1,2,3)

1. 自连接实现

SELECT s1.id, s1.score,
       (SELECT COUNT(*)
        FROM scores s2
        WHERE s2.score >= s1.score) AS rank
FROM scores s1
ORDER BY s1.score DESC;

解释

  • 使用 COUNT(*) 计算所有更高或相等分数
  • 结果示例:1,2,3

2. 变量实现

SELECT s.score, (@cur_rank := @cur_rank + 1) ranking
FROM score s, (SELECT @cur_rank := 0) r
ORDER BY score DESC;

解释

  • 每次查询直接递增计数器 @cur_rank

三、不连续并列排名

需求:相同值共享排名,后续排名跳过并列数(如 1,1,3)

1. 自连接实现

SELECT s1.id, s1.score,
       (SELECT COUNT(*)
        FROM (SELECT DISTINCT score FROM scores) s2
        WHERE s2.score > s1.score) + 1 AS rank
FROM scores s1
ORDER BY s1.score DESC;

解释

  • 子查询先提取唯一分数
  • 计算严格更高的唯一分数数量

2. 变量实现

SELECT s.score,
@rank_counter := @rank_counter + 1,
IF(@pre_score = s.score, @cur_rank, @cur_rank := @rank_counter) ranking,
@pre_score := s.score
FROM score s, (SELECT @cur_rank :=0, @pre_score := NULL, @rank_counter := 0) r
ORDER BY s.score DESC;

解释

  • @rank_counter 全局递增
  • @cur_rank 仅在分数变化时更新

四、分组连续不并列排名

需求:分组内唯一连续排名(如组 1:1,2,3;组 2:1,2)

1. 自连接实现

SELECT s1.group_id, s1.id, s1.score,
       (SELECT COUNT(*)
        FROM scores s2
        WHERE s2.group_id = s1.group_id 
          AND s2.score >= s1.score) AS rank
FROM scores s1
ORDER BY s1.group_id, s1.score DESC;

解释

  • 增加 group_id 条件限定分组范围

2. 变量实现

SELECT s.course_id, s.score,
IF(@pre_course_id = s.course_id, @cur_rank := @cur_rank + 1, @cur_rank := 1) ranking,
@pre_course_id := s.course_id
FROM score s, (SELECT @cur_rank := 0, @pre_course_id := NULL) r
ORDER BY course_id, score DESC;

解释

  • 检测分组切换时重置计数器

五、分组连续并列排名

需求:分组内相同值共享排名,总排名连续(如组 1:1,1,3;组 2:1,2)

1. 自连接实现

SELECT s1.group_id, s1.id, s1.score,
       (SELECT COUNT(DISTINCT s2.score)
        FROM scores s2
        WHERE s2.group_id = s1.group_id 
          AND s2.score >= s1.score) AS rank
FROM scores s1
ORDER BY s1.group_id, s1.score DESC;

解释

  • 分组内去重分数计算排名

2. 变量实现

SELECT s.course_id, s.score,
IF(@pre_course_id = s.course_id,
   IF(@pre_score = s.score, @cur_rank, @cur_rank := @cur_rank + 1),
   @cur_rank := 1) ranking,
@pre_score := s.score,
@pre_course_id := s.course_id
FROM score s, (SELECT @cur_rank :=0, @pre_score := NULL, @pre_course_id := NULL) r
ORDER BY course_id, score DESC;

解释

  • 双重条件判断分组和分数变化

六、分组不连续并列排名

需求:分组内相同值共享排名,后续排名跳过并列数(如组 1:1,1,3;组 2:1,2)

1. 自连接实现

SELECT s1.group_id, s1.id, s1.score,
       (SELECT COUNT(*)
        FROM (SELECT DISTINCT score FROM scores WHERE group_id = s1.group_id) s2
        WHERE s2.score > s1.score) + 1 AS rank
FROM scores s1
ORDER BY s1.group_id, s1.score DESC;

解释

  • 分组内先提取唯一分数

2. 变量实现

SELECT s.course_id, s.score,
IF(@pre_course_id = s.course_id,
   @rank_counter := @rank_counter + 1,
   @rank_counter := 1) temp1,
IF(@pre_course_id = s.course_id,
   IF(@pre_score = s.score, @cur_rank, @cur_rank := @rank_counter),
   @cur_rank := 1) ranking,
@pre_score := s.score,
@pre_course_id := s.course_id
FROM score s, (SELECT @cur_rank := 0, @pre_course_id := NULL, 
               @pre_score := NULL, @rank_counter := 1) r
ORDER BY s.course_id, s.score DESC;

解释

  • 引入 @rank_counter 跟踪全局位置

总结对比

排名类型自连接核心逻辑变量核心逻辑结果特征
连续并列COUNT(DISTINCT) 去重比较上一条分数允许重复,连续递增
连续不并列COUNT(*) 全量计数直接递增计数器无重复,连续递增
不连续并列唯一分数排序计数双重变量跟踪允许重复,跳过并列数
分组排名添加 group_id 条件检测分组切换重置变量分组内独立计算