web-dev-qa-db-ja.com

スコアテーブルでユーザーのランクを取得する

ハイスコ​​アを保存する非常にシンプルなMySQLテーブルがあります。それはそのように見えます:

Id     Name     Score

ここまでは順調ですね。問題は、ユーザーのランクを取得するにはどうすればよいですか?たとえば、ユーザーにNameまたはIdがあり、そのランクを取得したい場合、すべての行がScoreの降順で序数で並べられます。

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

この場合、Assemは3番目に高いスコアを獲得したため、ランクは3になります。

クエリは、必要なランク(のみ)を含む1行を返す必要があります。

32
Michael
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

スコアリストを取得するためのスキャンが1つあり、それを使用して別のスキャンを実行するか、有用な処理を実行します。 score列のインデックスは、大きなテーブルでのパフォーマンスに役立ちます。

35
cairnz

複数のエントリが同じスコアを持っている場合、次のランクは連続していてはなりません。次のランクは、同じランクを共有するスコアの数だけ増加する必要があります。

このようなスコアを表示するには、2つのランク変数が必要です

  • 表示する変数のランク付け
  • 計算するランク変数

これは、同順位のランキングのより安定したバージョンです。

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が同じランクを持っていることに注意してください。ランクは連続していないことにも注意してください。

試してみる !!!

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

1つのオプションは、USER変数を使用することです。

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

受け入れられた回答 には潜在的な問題があります。同じスコアが2つ以上ある場合、ランキングにギャップが生じます。この変更例では:

 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はありません。

ランキングにギャップがないことを確認する場合は、GROUP_CONCATDISTINCTを使用して、異なるスコアのリストを作成します。

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
4
Mahesh Lakher

これが最良の答えです。

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
FamerJoe

このソリューションは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
3
Arvind07

次の作業(テーブルがスコアと呼ばれていると仮定)しませんか?

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