it-swarm.com.de

Holen Sie sich den Rang eines Benutzers in einer Punktetabelle

Ich habe eine sehr einfache MySQL-Tabelle, in der ich Highscores speichere. Es sieht so aus:

Id     Name     Score

So weit, ist es gut. Die Frage ist: Wie erhalte ich den Rang eines Benutzers? Zum Beispiel habe ich einen Benutzer Name oder Id und möchte seinen Rang erhalten, wobei alle Zeilen in absteigender Reihenfolge für Score ordinal geordnet sind.

Ein Beispiel

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

In diesem Fall wäre der Rang von Assem 3, da er die dritthöchste Punktzahl erhalten hat.

Die Abfrage sollte eine Zeile zurückgeben, die (nur) den erforderlichen Rang enthält.

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

gibt diese Liste:

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

Einzelne Punktzahl erhalten:

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'

Gibt dieses Ergebnis:

id name score rank
5 Assem 99 3

Sie haben einen Scan, um die Score-Liste zu erhalten, und einen weiteren Scan oder versuchen, etwas Nützliches damit zu tun. Ein Index für die Spalte score würde die Leistung bei großen Tabellen verbessern.

35
cairnz

Wenn mehrere Einträge dieselbe Punktzahl haben, sollte der nächste Rang nicht aufeinanderfolgend sein. Der nächste Rang sollte um die Anzahl der Punkte erhöht werden, die denselben Rang haben.

Um solche Ergebnisse anzuzeigen, sind zwei Rangvariablen erforderlich

  • anzuzeigende Rangvariable
  • rangvariable zu berechnen

Hier ist eine stabilere Version des Rankings mit Bindungen:

SET @rnk=0; SET @rank=0; SET @curscore=0;
SELECT score,ID,rank FROM
(
    SELECT AA.*,BB.ID,
    (@rnk:[email protected]+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;

Probieren wir dies mit Beispieldaten aus. Hier sind die Beispieldaten:

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);

Laden wir die Beispieldaten

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

Als nächstes initialisieren wir die Benutzervariablen:

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)

Hier ist die Ausgabe der Abfrage:

mysql> SELECT score,ID,rank FROM
    -> (
    ->     SELECT AA.*,BB.ID,
    ->     (@rnk:[email protected]+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)

Bitte beachten Sie, dass mehrere IDs mit derselben Punktzahl denselben Rang haben. Beachten Sie auch, dass der Rang nicht aufeinanderfolgend ist.

Versuche es !!!

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

Eine Möglichkeit wäre die Verwendung von USER-Variablen:

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

Das akzeptierte Antwort hat ein potentielles Problem. Wenn es zwei oder mehr identische Bewertungen gibt, gibt es Lücken in der Rangliste. In diesem modifizierten Beispiel:

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

Die Punktzahl 58 hat Rang 5 und es gibt keinen Rang 4.

Wenn Sie sicherstellen möchten, dass die Ranglisten keine Lücken aufweisen, verwenden Sie DISTINCT in GROUP_CONCAT um eine Liste mit unterschiedlichen Punktzahlen zu erstellen:

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

Ergebnis:

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

Dies funktioniert auch, um den Rang eines einzelnen Benutzers zu erhalten:

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'

Ergebnis:

id name score rank
 2  Boo   58    4
4
Mahesh Lakher

Hier ist die beste Antwort:

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 ;

Diese Abfrage gibt Folgendes zurück:

3

3
FamerJoe

Diese Lösung ergibt die DENSE_RANK bei Krawatten:

SELECT *,
IF (@score=s.Score, @rank:[email protected], @rank:[email protected]nk+1) rank,
@score:=s.Score score
FROM scores s,
(SELECT @score:=0, @rank:=0) r
ORDER BY points DESC
3
Arvind07

Würde das nicht funktionieren (vorausgesetzt, Ihre Tabelle heißt Scores)?

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