文章目录
  1. 1. 题目
  2. 2. 解题思路
  3. 3. 注意事项
  4. 4. 构造测试数据
  5. 5. 一种答案
  6. 6. 另一种答案

题目

#178 Rank Scores

解题思路

典型的 Ranking 类问题,后面很多其他题目也会用到这个方法。就是构造用户自定义变量 prev_value 记录前值,rank_count 记录当前排名。

注意事项

  • 题目要求 Score 相同的排名要相同,且排名是连续序号例如 “1 - 1 - 2 - 3”

    计算起来复杂不少,要考虑前后 Score 相同排名相同的情况

构造测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE TABLE IF NOT EXISTS Scores (
Id INT,
Score DOUBLE
);

DELETE FROM Scores;

INSERT INTO Scores VALUES
(1, 3.50),
(2, 3.65),
(3, 4.00),
(4, 3.85),
(5, 4.00),
(6, 3.65);

预期结果:

Score Rank
4.00 1
4.00 1
3.85 2
3.65 3
3.65 3
3.50 4

一种答案

完整答案:

1
2
3
4
5
6
7
8
9
10
11
12
-- Runtime: 927 ms
SELECT
Score, Rank
FROM
(SELECT
Score,
@prev_value AS prevValue,
@rank_count:=IF(Score < @prev_value, @rank_count + 1, @rank_count) AS Rank,
@prev_value:=Score AS nowValue
FROM
Scores, (SELECT @rank_count:=1) r, (SELECT @prev_value:=NULL) p
ORDER BY Score DESC) tmp

我们分步来看。第一步就是得到 tmp 表:

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT 
Id,
Score,
@prev_value AS prevValue,
@rank_count:=IF(Score < @prev_value,
@rank_count + 1,
@rank_count) AS Rank,
@prev_value:=Score AS nowValue
FROM
Scores,
(SELECT @rank_count:=1) r,
(SELECT @prev_value:=NULL) p
ORDER BY Score DESC

第一步的结果是:

Id Score prevValue Rank nowValue
3 4 NULL 1 4
5 4 4 1 4
4 3.85 4 2 3.85
2 3.65 3.85 3 3.65
6 3.65 3.65 3 3.65
1 3.5 3.65 4 3.5

接着第二步就是去除多余的列,因为结果只需要 Score 和 Rank 两列。

另一种答案

思想和前面一种一样的,用了比较 tricky 的 CASE WHEN 所以只用一层 SELECT。个人认为还是上面一种方法比较直观。

特别需要注意的时其中的 ELSE @rank_count:=@rank_count + 1,对于大部分数据没有这行也是正确的。但是若 Score 是 0,例如输入数据是 (1, 0.0) 若没有这个 ELSE 则结果是 NULL。原理暂时还没搞明白

1
2
3
4
5
6
7
8
9
10
11
12
13
-- Runtime: 878 ms
SELECT
Score,
CASE
WHEN @prev_value = Score THEN @rank_count
WHEN @prev_value:=Score THEN @rank_count:=@rank_count + 1
ELSE @rank_count:=@rank_count + 1
END AS Rank
FROM
Scores,
(SELECT @rank_count:=0) r,
(SELECT @prev_value:=NULL) p
ORDER BY Score DESC

本博客微信公众号

文章目录
  1. 1. 题目
  2. 2. 解题思路
  3. 3. 注意事项
  4. 4. 构造测试数据
  5. 5. 一种答案
  6. 6. 另一种答案