Answers:
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
列上的索引将有助于提高大型表的性能。
SELECT 1 + COUNT(*) AS rank FROM scores WHERE score > (SELECT score FROM scores WHERE name='Assem')
。哪个“仅”计算得分高于当前条目的条目数。(如果添加,DISTINCT
您将获得排名没有差距。)
当多个条目具有相同分数时,下一个等级不应连续。下一个等级应增加共享相同等级的分数的数量。
要显示这样的分数,需要两个等级变量
这是带有联系的排名的更稳定版本:
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的排名相同。另请注意,等级不是连续的。
试试看 !!!
该接受的答案有一个潜在的问题。如果存在两个或两个以上相同的分数,则排名中将存在差距。在此修改后的示例中:
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
和子查询,可以极大地优化单个用户的排名查询。请参阅我的评论在接受的答案
这是最好的答案:
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
(SELECT GROUP_CONCAT(score) FROM TheWholeTable)
不是最好的方法。并且它可能与创建的行的大小有关。