Ruft den Rang eines Benutzers in einer Punktetabelle ab


31

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 bekomme ich den Rang eines Benutzers? Ich habe zum Beispiel einen Benutzer Nameoder Idund möchte seinen Rang erhalten, bei dem alle Zeilen nach Ordnungszahl absteigend für den geordnet sind Score.

Ein Beispiel

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

In diesem Fall Assemwäre der Rang 3, weil er die dritthöchste Punktzahl hat.

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

Antworten:


31
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

Punkte für eine einzelne Person 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 müssen einen Scan durchführen, um die Score-Liste zu erhalten, und einen weiteren Scan oder versuchen, etwas Nützliches daraus zu machen. Ein Index für die scoreSpalte würde die Leistung bei großen Tabellen verbessern.


3
Die Korrelation (SELECT GROUP_CONCAT(score) FROM TheWholeTable)ist nicht der beste Weg. Möglicherweise liegt ein Problem mit der Größe der erstellten Zeile vor.
ypercubeᵀᴹ

2
Bei Unentschieden schlägt dies fehl.
Arvind07

Die einzige Person Punktzahl Abfrage ist extrem langsam für größere Tabellen .. Eine viel bessere Abfrage Rang zu bestimmen (mit Lücken für Beziehungen) für eine einzelne Person Punktzahl ist: SELECT 1 + COUNT(*) AS rank FROM scores WHERE score > (SELECT score FROM scores WHERE name='Assem'). Wobei "nur" die Anzahl der Einträge mit einer höheren Punktzahl als der aktuelle Eintrag zählt. (Wenn Sie hinzufügen, erhalten DISTINCTSie den Rang ohne Lücken ..)
Paul

WICHTIG: GROUP_CONTAT hat eine Standardbeschränkung von 1024 Zeichen. Bei großen Datenmengen führt dies zu falschen Rängen. Beispielsweise wird es möglicherweise auf Rang 100 gestoppt und meldet 0 als Rang
0plus1.

30

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

Um solche Ergebnisse anzuzeigen, sind zwei Rangvariablen erforderlich

  • Rangvariable zum Anzeigen
  • Rangvariable zu berechnen

Hier ist eine stabilere Version des Rankings mit Gleichstand:

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;

Lassen Sie uns dies mit Beispieldaten ausprobieren. 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:=@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)

Bitte beachten Sie, dass mehrere IDs, die dieselbe Punktzahl haben, denselben Rang haben. Beachten Sie auch, dass der Rang nicht fortlaufend ist.

Versuche es !!!


Ist dies sicher, da sitzungsbezogene Variablen verwendet werden, wenn beispielsweise mehrere Endbenutzer gleichzeitig die Anzeigetafel anfordern? Kann es sein, dass die Ergebnismenge unterschiedliche Ergebnisse liefert, weil ein anderer Benutzer diese Abfrage ebenfalls ausführt? Stellen Sie sich eine API vor, die von vielen Clients gleichzeitig aufgerufen wird.
Xaero Degreaz

@XaeroDegreaz Du hast recht, es ist möglich. Stellen Sie sich vor, Sie berechnen die Ränge für ein Spiel. Ein Benutzer fragt nach Rang und ein anderer Benutzer fragt 5 Sekunden, nachdem eine Person den Highscore übertroffen oder die besten X-Scores eingegeben hat. Dies kann jedoch auch passieren, wenn das Ranking auf Anwendungsebene und nicht auf Serverebene erfolgt.
RolandoMySQLDBA

Danke für die Antwort. Mein Anliegen ist nicht wirklich, ob sich die Daten im Laufe der Zeit organisch verschieben oder nicht. Mein Anliegen ist, dass mehrere Benutzer, die die Abfrage ausführen, die in den sitzungsspezifischen Variablen gespeicherten Daten ändern / überschreiben, während andere Benutzer die Abfrage ebenfalls ausführen. Ist das sinnvoll?
Xaero Degreaz

@XaeroDegreaz, das ist die Schönheit der Sitzungsbereichsvariablen. Sie sind nur in Ihrer Sitzung und niemand anderes. Sie werden keine Sitzungsvariablen von anderen Benutzern sehen und niemand wird Ihre Sitzungsvariablen sehen.
RolandoMySQLDBA

Okay, das war es, woran ich geglaubt habe - dass Sitzungsvariablen für die Verbindung relevant sind und eine einzelne Verbindung nicht von mehr als einer Person gleichzeitig belegt werden kann. Sobald die Verbindung frei ist oder zurück in den Pool geworfen wurde, kann ein anderer Benutzer auf die Verbindung zugreifen und die Sitzungsvariablen werden neu initialisiert (wenn diese Abfrage ausgeführt wird). Nochmals vielen Dank für die Informationen.
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

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

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

4

Die akzeptierte Antwort weist ein potenzielles Problem auf. Wenn es zwei oder mehr identische Punkte gibt, gibt es Lücken in der Rangfolge. 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 es keine Lücken in der Rangliste, die Verwendung DISTINCTin der GROUP_CONCATeine Liste von verschiedenen Noten zu bauen:

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

Die COUNTRangabfrage eines einzelnen Benutzers kann enorm optimiert werden, indem stattdessen eine Unterabfrage verwendet wird. Siehe meinen Kommentar bei der akzeptierten Antwort
Paul

Gute Note und Verbesserung. funktioniert sehr gut
SMMousavi

3

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


Ich habe ein kleines Problem damit, dachte ich. Beispiel: Wenn die ersten beiden Benutzer unterschiedliche Bewertungen haben und der Rest 0, ist die Rangfolge für die Personen mit der Nullbewertung # 4 anstelle von # 3. Aber der erste bekommt richtig # 1 und der zweite # 2. Irgendwelche Ideen?
Fersarr

3

Diese Lösung ergibt DENSE_RANKim Falle von Unentschieden:

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

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

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

-4

Ich habe diese, die die gleichen Ergebnisse wie die mit Variablen gibt. Es funktioniert mit Krawatten und es kann schneller sein:

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

Ich habe es nicht getestet, aber ich verwende eines, das perfekt funktioniert, und das ich an die Variablen angepasst habe, die Sie hier verwendet haben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.