it-swarm.it

Ottieni il grado di un utente in una tabella dei punteggi

Ho una tabella MySQL molto semplice in cui salvo i punteggi migliori. Sembra che:

Id     Name     Score

Fin qui tutto bene. La domanda è: come posso ottenere il ranking degli utenti? Ad esempio, ho un utente Name o Id e voglio ottenere il suo grado, in cui tutte le righe sono ordinate in ordine decrescente per Score.

Un esempio

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

In questo caso, il grado di Assem sarebbe 3, poiché ha ottenuto il 3 ° punteggio più alto.

La query dovrebbe restituire una riga, che contiene (solo) il grado richiesto.

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

dà questo elenco:

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

Ottenere un punteggio per una sola persona:

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'

Dà questo risultato:

id name score rank
5 Assem 99 3

Avrai una scansione per ottenere l'elenco dei punteggi e un'altra scansione o cercare di fare qualcosa di utile con esso. Un indice sulla colonna score aiuterebbe le prestazioni su tabelle di grandi dimensioni.

35
cairnz

Quando più voci hanno lo stesso punteggio, il rango successivo non dovrebbe essere consecutivo. Il rango successivo dovrebbe essere incrementato del numero di punteggi che condividono lo stesso rango.

Per visualizzare punteggi del genere sono necessarie due variabili di classificazione

  • rango variabile da visualizzare
  • rango variabile da calcolare

Ecco una versione più stabile della classifica con legami:

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;

Proviamo con dati di esempio. Prima Ecco i dati di esempio:

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

Carichiamo i dati di esempio

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

Quindi, inizializzare le variabili utente:

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)

Ora, ecco l'output della query:

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)

Si noti che più ID che condividono lo stesso punteggio hanno lo stesso rango. Si noti inoltre che il rango non è consecutivo.

Provaci !!!

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

Un'opzione sarebbe quella di utilizzare le variabili USER:

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

risposta accettata ha un potenziale problema. Se ci sono due o più punteggi identici, ci saranno delle lacune nella classifica. In questo esempio modificato:

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

Il punteggio di 58 ha il grado 5 e non esiste il grado 4.

Se vuoi assicurarti che non ci siano lacune nelle classifiche, usa DISTINCT in GROUP_CONCAT per creare un elenco di punteggi distinti:

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

Risultato:

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

Questo funziona anche per ottenere il rango di un singolo utente:

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'

Risultato:

id name score rank
 2  Boo   58    4
4
Mahesh Lakher

Ecco la risposta migliore:

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 ;

Questa query restituirà:

3

3
FamerJoe

Questa soluzione fornisce il DENSE_RANK in caso di vincoli:

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

Non funzionerebbe il seguente (supponendo che il tuo tavolo sia chiamato Punteggi)?

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