MySQL分数排名

Posted willem_chen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL分数排名相关的知识,希望对你有一定的参考价值。

SQL架构

Create table If Not Exists Scores (Id int, Score DECIMAL(3,2));
Truncate table Scores
insert into Scores (Id, Score) values ('1', '3.5');
insert into Scores (Id, Score) values ('2', '3.65');
insert into Scores (Id, Score) values ('3', '4.0');
insert into Scores (Id, Score) values ('4', '3.85');
insert into Scores (Id, Score) values ('5', '4.0');
insert into Scores (Id, Score) values ('6', '3.65');

题目描述

编写一个 SQL 查询来实现分数排名。

如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。

+----+-------+
| Id | Score |
+----+-------+
| 1  | 3.50  |
| 2  | 3.65  |
| 3  | 4.00  |
| 4  | 3.85  |
| 5  | 4.00  |
| 6  | 3.65  |
+----+-------+

例如,根据上述给定的 Scores 表,你的查询应该返回(按分数从高到低排列):

+-------+------+
| Score | Rank |
+-------+------+
| 4.00  | 1    |
| 4.00  | 1    |
| 3.85  | 2    |
| 3.65  | 3    |
| 3.65  | 3    |
| 3.50  | 4    |
+-------+------+

重要提示:对于 MySQL 解决方案,如果要转义用作列名的保留字,可以在关键字之前和之后使用撇号。

例如 `Rank`

题解

答1:Mysql比较好理解的一种写法

对于a表中的每一个分数score,找出b表中有多少个大于或等于该分数的不同的分数,然后按降序排列

select 
    a.Score as score , 
    (select count(distinct b.Score) from Scores b where b.Score >=a.Score) as rank
from Scores a order by Score DESC;

+-------+------+
| score | rank |
+-------+------+
|  4.00 |    1 |
|  4.00 |    1 |
|  3.85 |    2 |
|  3.65 |    3 |
|  3.65 |    3 |
|  3.50 |    4 |
+-------+------+
6 rows in set (0.00 sec)

答2

select s1.Score, 
(
	select count(distinct s2.Score)  from Scores s2  where s2.Score > s1.Score )
	+1 `Rank` from Scores s1 order by Score DESC;
	
+-------+------+
| Score | Rank |
+-------+------+
|  4.00 |    1 |
|  4.00 |    1 |
|  3.85 |    2 |
|  3.65 |    3 |
|  3.65 |    3 |
|  3.50 |    4 |
+-------+------+
6 rows in set (0.00 sec)

答3:临时变量 + 联查 实现

我看题解了里面大部分都是用了 DENSE_RANK() 函数实现的,不过 mysql 5.x 版本好像不支持,所以用临时变量加联查的方式来实现感觉也不错,虽然通不过本题,但是应该是正确的做法,我的mysql版本是 5.6.45

第一步:先查分数对应的排名

select 
@Rank := @Rank + 1 AS Rank,
s.Score
from 
( select @Rank := 0 ) m,
( select Score from Scores group by Score desc) s

+------+-------+
| Rank | Score |
+------+-------+
|    1 |  4.00 |
|    2 |  3.85 |
|    3 |  3.65 |
|    4 |  3.50 |
+------+-------+
4 rows in set, 1 warning (0.00 sec)

第二步:和原有的Scores表联查,再根据rank字段排序即可

select s.Score, r.Rank 
from Scores s
left join
(
    select 
    @Rank := @Rank + 1 AS Rank,
    s.Score
    from 
    ( select @Rank := 0 ) m,
    ( select Score from Scores group by Score desc) s
) r
on s.Score = r.Score
order by r.Rank;

+-------+------+
| Score | Rank |
+-------+------+
|  4.00 |    1 |
|  4.00 |    1 |
|  3.85 |    2 |
|  3.65 |    3 |
|  3.65 |    3 |
|  3.50 |    4 |
+-------+------+
6 rows in set, 1 warning (0.00 sec)

( select @Rank := 0 ) m, 这个表,是在给临时变量赋初始值。

知识点

MySQL的@与@@区别

@x 是 用户自定义的变量  (User variables are written as @var_name)

@@x 是 global或session变量  (@@global  @@session )

@@查看全局变量:
select   @@log_error;

@设置全局变量值:

mysql> SET @t1=0, @t2=0, @t3=0;

mysql> SELECT @t1:=(@t2:=1)+@t3:=4,@t1,@t2,@t3;

mysql中:=和=的区别

=

只有在 set 和 update 时才是和 := 一样,赋值的作用,其它都是等于的作用。鉴于此,用变量实现行号时,必须用:=

:=
不只在set和update时时赋值的作用,在select也是赋值的作用。

@num:=@num+1,:=是赋值的作用,所以,先执行@num+1,然后再赋值给@num,所以能正确实现行号的作用。


@num=@num+1,此时=是等于的作用,@num不等于@num+1,所以始终返回0,如果改为@num=@num,始终返回1了。mysql数据库中,用1表示真,0表示假。

以上是关于MySQL分数排名的主要内容,如果未能解决你的问题,请参考以下文章

MySQL分数排名

mysql中的用户按他们的分数排名

MySQL 基于得分和时间的排名

向 Google Play 游戏排行榜提交分数并显示新排名

mysql--分数排名

从使用 PHP 的 MySQL 数据库到 iPhone 的分数列表中获取排名