一、实现步骤
- 1、实现自然排名
SELECT
id, name, score,
-- 排名变量每次+1,实现自然排名
@curr_row_rank := @curr_row_rank + 1 AS rank
FROM
scores,
(SELECT
-- 定义一个变量,每条记录+1,用于标识自然的排名
@curr_row_rank := 0
) vars
ORDER BY
-- 排序必须存在
score DESC
- 2、实现并列排名
SELECT
id, name, score,
-- CASE 只返回第一个条件成立的WHEN所对应THEN的值
CASE
-- 对比上条记录和当前记录的分数,如分数相等,则排名不变,返回和上条记录相同的排名
WHEN @pref_row_score = score THEN @curr_row_rank
-- 如果上一个WHEN没有进入,即分数不等,则将当前分数赋值给标识上条记录分数的变量,同时将排名+1,返回+1后的排名
WHEN @pref_row_score := score THEN @curr_row_rank := @curr_row_rank + 1
-- 处理分数为0没有排名的问题(当WHEN中为变量赋值为0和Null时,WHEN不成立,不进入THEN,即上一个WHEN/THEN不执行)
WHEN @pref_row_score = 0 THEN @curr_row_rank := @curr_row_rank + 1
END AS rank,
-- 将这个变量作为排名(替代CASE返回值排名),可以在添加了上一个WHEN时兼容分数为Null的情况,将0分和Null并列为最后一名
@curr_row_rank AS curr_row_rank,
@pref_row_score AS pref_row_score
FROM
scores,
(SELECT
-- 定义一个变量,在当前记录分数和上条记录分数不同时+1,用于标识排名
@curr_row_rank := 0,
-- 定义一个变量,保存上一条记录的分数,用于当前记录与上条记录对比
@pref_row_score := NULL
) vars
ORDER BY
-- 排序必须存在
score DESC
- 3、实现并列顺延
SELECT
id, name, score,
-- 每条记录自增一次,用于并列之后的记录获取跳过占用的名次
@incr_row_rank := @incr_row_rank + 1 AS incr_row_rank,
-- CASE 只返回第一个条件成立的WHEN所对应THEN的值
CASE
-- 对比上条记录和当前记录的分数,如分数相等,则排名不变,返回和上条记录相同的排名
WHEN @pref_row_score = score THEN @curr_row_rank
-- 如果上一个WHEN没有进入,即分数不等,则将当前记录分数赋值给上条记录分数的变量(用于下条记录比较),同时将排名+1,返回+1后的排名
WHEN @pref_row_score := score THEN @curr_row_rank := @incr_row_rank
-- 处理分数为0没有排名的问题(当WHEN中为变量赋值为0和Null时,WHEN不成立,不进入THEN,即上一个WHEN/THEN不执行)
WHEN @pref_row_score = 0 THEN @curr_row_rank := @incr_row_rank
END AS rank,
-- 将这个变量作为排名(替代返CASE回值排名),可以在添加了上一个WHEN时兼容分数为Null的情况,将0分和Null并列为最后一名
@curr_row_rank AS curr_row_rank,
@pref_row_score AS pref_row_score
FROM
scores,
(SELECT
-- 定义一个变量,在当前记录分数和上条记录分数相同时,赋值上条记录的名次,否则赋值自然排名的名次(跳过并列占用)
@curr_row_rank := 0,
-- 定义一个变量,保存上一条记录的分数,用于当前记录与上条记录对比
@pref_row_score := NULL,
-- 定义一个变量,保存自增的自然排序的排名,用于并列之后的记录获取跳过并列占用的排名(初始为0,为当前记录服务)
@incr_row_rank := 0
) vars
ORDER BY
-- 排序必须存在
score DESC
二、其他实现
- 1、IF函数方式
- 1.1、并列连续
SELECT
id, name, score,
-- IF(exp1, exp2, exp3):当exp1成立即true时,返回exp2的值,当exp1不成立即false是,返回exp2的值
-- 如果当前记录的分数和上条记录的分数相同,则返回和上条记录相同的排名,否则返回自增的排名(并列之后跳过占用的名次)
@curr_row_rank := IF(@pref_row_score = score, @curr_row_rank, @curr_row_rank := @curr_row_rank + 1) AS rank,
-- 将当前记录的分数赋值为上条记录的分数,用于下条记录比较
@pref_row_score := score AS pref_row_score
-- 兼容0分,不兼容Null分
FROM
scores,
(SELECT
-- 定义一个变量,在当前记录分数和上条记录分数不同时+1,用于标识排名
@curr_row_rank := 0,
-- 定义一个变量,保存上一条记录的分数,用于当前记录与上条记录对比
@pref_row_score := NULL
) vars
ORDER BY
-- 排序必须存在
score DESC
- 1.2、并列顺延
SELECT
id, name, score,
-- IF(exp1, exp2, exp3):当exp1成立即true时,返回exp2的值,当exp1不成立即false时,返回exp2的值
-- 如果当前记录的分数和上条记录的分数相同,则返回和上条记录相同的排名,否则返回自增的排名(并列之后跳过占用的名次)
@curr_row_rank := IF ( @pref_row_score = score, @curr_row_rank, @incr_row_rank ) AS rank,
-- 每条记录自增一次,用于并列之后的记录回归跳过占用的名次
@incr_row_rank := @incr_row_rank + 1 AS incr_row_rank,
-- 将当前记录的分数赋值为上条记录的分数,用于下条记录比较
@pref_row_score := score AS pref_row_score
-- 兼容0分,不兼容Null分
FROM
scores,
(SELECT
-- 定义一个变量,在当前记录分数和上条记录分数相同时,赋值上条记录的名次,否则赋值自然排名的名次(跳过并列占用)
@curr_row_rank := 0,
-- 定义一个变量,保存上一条记录的分数,用于当前记录分数与上条记录分数对比
@pref_row_score := NULL,
-- 定义一个变量,保存自增的自然排序的排名,用于并列之后的记录获取跳过并列占用的排名(初始为1,为下条记录服务)
@incr_row_rank := 1
) vars
ORDER BY
-- 排序必须存在
score DESC
- 2、子查询方式
- 2.1、并列连续
SELECT
id, name, score,
-- 大于等于此分数的不重复的个数
( SELECT count( distinct score ) FROM scores WHERE score >= s.score ) AS rank
-- 不兼容Null分,排名为0名
FROM
scores s
ORDER BY
score DESC;
- 2.2、并列顺延
SELECT
id, name, score,
-- 大于此分数的不重复的个数加一
( SELECT count(score) + 1 FROM scores WHERE score > s.score ) AS rank
-- 不兼容Null分,排名为1名
FROM
scores s
ORDER BY
score DESC;