MySQL - 排名实现

一、实现步骤

  • 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;

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章