获取分数表中用户的排名


31

我有一个非常简单的MySQL表,用于保存高分。看起来像这样:

Id     Name     Score

到目前为止,一切都很好。问题是:如何获得用户等级?例如,我有一个用户NameId并想要获得他的排名,其中所有行都按序排列Score

一个例子

Id  Name    Score
1   Ida     100
2   Boo     58
3   Lala    88
4   Bash    102
5   Assem   99

在这种情况下,Assem的排名为3,因为他获得了第三高的分数。

查询应返回一行,其中仅包含所需的Rank。

Answers:


31
SELECT id, name, score, FIND_IN_SET( score, (
SELECT GROUP_CONCAT( score
ORDER BY score DESC ) 
FROM scores )
) AS rank
FROM scores

给出以下列表:

id name  score rank
1  Ida   100   2
2  Boo    58   5
3  Lala   88   4
4  Bash  102   1
5  Assem  99   3

获得单人得分:

SELECT id, name, score, FIND_IN_SET( score, (    
SELECT GROUP_CONCAT( score
ORDER BY score DESC ) 
FROM scores )
) AS rank
FROM scores
WHERE name =  'Assem'

给出以下结果:

id name score rank
5 Assem 99 3

您将进行一次扫描以获取得分列表,然后进行另一次扫描或尝试对其进行有用的操作。score列上的索引将有助于提高大型表的性能。


3
关联(SELECT GROUP_CONCAT(score) FROM TheWholeTable)不是最好的方法。并且它可能与创建的行的大小有关。
ypercubeᵀᴹ

2
如果是平局,这将失败。
Arvind07年

对于较大的表,单人得分查询的速度非常慢。确定单人得分的排名(具有平局)的更好的查询是:SELECT 1 + COUNT(*) AS rank FROM scores WHERE score > (SELECT score FROM scores WHERE name='Assem')。哪个“仅”计算得分高于当前条目的条目数。(如果添加,DISTINCT您将获得排名没有差距。)
Paul

重要说明:GROUP_CONTAT的默认限制为1024个字符,在大型数据集上,这将导致错误的排名,例如,它可能会在排名100处停止,然后将排名报告为0
0plus1

30

当多个条目具有相同分数时,下一个等级不应连续。下一个等级应增加共享相同等级的分数的数量。

要显示这样的分数,需要两个等级变量

  • 显示的排名变量
  • 计算等级变量

这是带有联系的排名的更稳定版本:

SET @rnk=0; SET @rank=0; SET @curscore=0;
SELECT score,ID,rank FROM
(
    SELECT AA.*,BB.ID,
    (@rnk:=@rnk+1) rnk,
    (@rank:=IF(@curscore=score,@rank,@rnk)) rank,
    (@curscore:=score) newscore
    FROM
    (
        SELECT * FROM
        (SELECT COUNT(1) scorecount,score
        FROM scores GROUP BY score
    ) AAA
    ORDER BY score DESC
) AA LEFT JOIN scores BB USING (score)) A;

让我们尝试使用示例数据。首先这是示例数据:

use test
DROP TABLE IF EXISTS scores;
CREATE TABLE scores
(
    id int not null auto_increment,
    score int not null,
    primary key (id),
    key score (score)
);
INSERT INTO scores (score) VALUES
(50),(40),(75),(80),(55),
(40),(30),(80),(70),(45),
(40),(30),(65),(70),(45),
(55),(45),(83),(85),(60);

让我们加载样本数据

mysql> DROP TABLE IF EXISTS scores;
Query OK, 0 rows affected (0.15 sec)

mysql> CREATE TABLE scores
    -> (
    ->     id int not null auto_increment,
    ->     score int not null,
    ->     primary key (id),
    ->     key score (score)
    -> );
Query OK, 0 rows affected (0.16 sec)

mysql> INSERT INTO scores (score) VALUES
    -> (50),(40),(75),(80),(55),
    -> (40),(30),(80),(70),(45),
    -> (40),(30),(65),(70),(45),
    -> (55),(45),(83),(85),(60);
Query OK, 20 rows affected (0.04 sec)
Records: 20  Duplicates: 0  Warnings: 0

接下来,让我们初始化用户变量:

mysql> SET @rnk=0; SET @rank=0; SET @curscore=0;
Query OK, 0 rows affected (0.01 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

现在,这是查询的输出:

mysql> SELECT score,ID,rank FROM
    -> (
    ->     SELECT AA.*,BB.ID,
    ->     (@rnk:=@rnk+1) rnk,
    ->     (@rank:=IF(@curscore=score,@rank,@rnk)) rank,
    ->     (@curscore:=score) newscore
    ->     FROM
    ->     (
    ->         SELECT * FROM
    ->         (SELECT COUNT(1) scorecount,score
    ->         FROM scores GROUP BY score
    ->     ) AAA
    ->     ORDER BY score DESC
    -> ) AA LEFT JOIN scores BB USING (score)) A;
+-------+------+------+
| score | ID   | rank |
+-------+------+------+
|    85 |   19 |    1 |
|    83 |   18 |    2 |
|    80 |    4 |    3 |
|    80 |    8 |    3 |
|    75 |    3 |    5 |
|    70 |    9 |    6 |
|    70 |   14 |    6 |
|    65 |   13 |    8 |
|    60 |   20 |    9 |
|    55 |    5 |   10 |
|    55 |   16 |   10 |
|    50 |    1 |   12 |
|    45 |   10 |   13 |
|    45 |   15 |   13 |
|    45 |   17 |   13 |
|    40 |    2 |   16 |
|    40 |    6 |   16 |
|    40 |   11 |   16 |
|    30 |    7 |   19 |
|    30 |   12 |   19 |
+-------+------+------+
20 rows in set (0.18 sec)

请注意,具有相同分数的多个ID的排名相同。另请注意,等级不是连续的。

试试看 !!!


由于这是使用会话范围的变量,如果有多个最终用户同时请求记分板,这是否安全?由于另一个用户也在执行此查询,结果集是否可能具有不同的结果?想象一下,此查询前面有一个API,有许多客户端一次将其击中。
Xaero Degreaz

@XaeroDegreaz是的,这是可能的。想象一下计算游戏排名。在某人击败高分或输入最高X分数后,一个用户查询等级,另一用户查询5秒。尽管如此,如果排名是在应用程序级别而不是服务器级别进行的,则可能会发生同样的情况。
RolandoMySQLDBA

谢谢回复。我真正关心的不是数据是否随时间自然地移动,我关心的是,执行查询的多个用户将修改/覆盖存储在会话范围变量中的数据,而其他用户也在执行查询。那有意义吗?
Xaero Degreaz

@XaeroDegreaz就是会话作用域变量的美。它们仅在您的会话中,其他人不在。您将看不到其他用户的会话变量,也没人会看到您的会话变量。
RolandoMySQLDBA

好的,这就是我倾向于相信的东西-会话变量的作用域是连接,并且一个连接不能一次被多个人占用。一旦释放了连接,或者将其扔回到了池中,另一个用户就可以跳上连接,并重新初始化会话变量(执行此查询时)。再次感谢您提供信息。
Xaero Degreaz

13
SELECT 
    id, 
    Name,
    1+(SELECT count(*) from table_name a WHERE a.Score > b.Score) as RNK,
    Score
FROM table_name b;

9

一种选择是使用USER变量:

SET @i=0;
SELECT id, name, score, @i:=@i+1 AS rank 
 FROM ranking 
 ORDER BY score DESC;

4

接受的答案有一个潜在的问题。如果存在两个或两个以上相同的分数,则排名中将存在差距。在此修改后的示例中:

 id name  score rank
 1  Ida   100   2
 2  Boo    58   5
 3  Lala   99   3
 4  Bash  102   1
 5  Assem  99   3

58分的得分为5,没有等级4。

如果您要确保排名没有差距,请使用DISTINCT中的GROUP_CONCAT来构建不同分数的列表:

SELECT id, name, score, FIND_IN_SET( score, (
SELECT GROUP_CONCAT( DISTINCT score
ORDER BY score DESC ) FROM scores)
) AS rank
FROM scores

结果:

id name  score rank
1  Ida   100   2
2  Boo    58   4
3  Lala   99   3   
4  Bash  102   1
5  Assem  99   3

这也适用于获得单个用户的排名:

SELECT id, name, score, FIND_IN_SET( score, (    
SELECT GROUP_CONCAT(DISTINCT score
ORDER BY score DESC ) 
FROM scores )
) AS rank
FROM scores
WHERE name =  'Boo'

结果:

id name score rank
 2  Boo   58    4

通过使用COUNT和子查询,可以极大地优化单个用户的排名查询。请参阅我的评论在接受的答案
保罗

良好的注释和增强。效果很好
SMMousavi

3

这是最好的答案:

SELECT 1 + (SELECT count( * ) FROM highscores a WHERE a.score > b.score ) AS rank FROM
highscores b WHERE Name = 'Assem' ORDER BY rank LIMIT 1 ;

该查询将返回:

3


我的想法有一个小问题。例如:如果前两个用户的得分不同,其余所有用户的得分均为0,则零得分人员的排名为#4,而不是#3。但是第一个正确地获得#1,第二个正确地获得。有任何想法吗?
fersarr 2014年

3

此解决方案提供了平局的DENSE_RANK情况:

SELECT *,
IF (@score=s.Score, @rank:=@rank, @rank:=@rank+1) rank,
@score:=s.Score score
FROM scores s,
(SELECT @score:=0, @rank:=0) r
ORDER BY points DESC

0

不会进行以下工作(假设您的表称为“分数”)吗?

SELECT COUNT(id) AS rank FROM Scores 
WHERE score <= (SELECT score FROM Scores WHERE Name = "Assem")

-4

我有这个,它给出的结果与带有变量的结果相同。它可以配合领带使用,并且可能更快:

SELECT COUNT(*)+1 as rank
FROM 
(SELECT score FROM scores ORDER BY score) AS sc
WHERE score <
(SELECT score FROM scores WHERE Name="Assem")

我没有测试它,但是我使用的是一个完美的作品,我使用您在这里使用的变量对此进行了调整。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.