Aggiornare il rango in una tabella di MySQL
Domanda
Ho la seguente struttura della tabella per una tabella Player
Table Player {
Long playerID;
Long points;
Long rank;
}
Supponendo che la playerid ed i punti sono valori validi, posso aggiornare il rango per tutti i giocatori in base al numero di punti in una singola query? Se due persone hanno lo stesso numero di punti, che dovrebbero legare per il rango.
UPDATE:
Sto usando Hibernate utilizzando la query suggerito come una query nativo. Hibernate non come l'utilizzo di variabili, in particolare il ':'. Qualcuno sa di alcuna soluzione alternativa? O non usando le variabili o di lavoro intorno di Hibernate limitazione in questo caso utilizzando HQL?
Soluzione
Una possibilità è quella di utilizzare una variabile classifica, come il seguente:
UPDATE player
JOIN (SELECT p.playerID,
@curRank := @curRank + 1 AS rank
FROM player p
JOIN (SELECT @curRank := 0) r
ORDER BY p.points DESC
) ranks ON (ranks.playerID = player.playerID)
SET player.rank = ranks.rank;
La parte JOIN (SELECT @curRank := 0)
permette inizializzazione delle variabili senza richiedere un comando SET
separata.
Per ulteriori approfondimenti su questo argomento:
Test Case:
CREATE TABLE player (
playerID int,
points int,
rank int
);
INSERT INTO player VALUES (1, 150, NULL);
INSERT INTO player VALUES (2, 100, NULL);
INSERT INTO player VALUES (3, 250, NULL);
INSERT INTO player VALUES (4, 200, NULL);
INSERT INTO player VALUES (5, 175, NULL);
UPDATE player
JOIN (SELECT p.playerID,
@curRank := @curRank + 1 AS rank
FROM player p
JOIN (SELECT @curRank := 0) r
ORDER BY p.points DESC
) ranks ON (ranks.playerID = player.playerID)
SET player.rank = ranks.rank;
Risultato:
SELECT * FROM player ORDER BY rank;
+----------+--------+------+
| playerID | points | rank |
+----------+--------+------+
| 3 | 250 | 1 |
| 4 | 200 | 2 |
| 5 | 175 | 3 |
| 1 | 150 | 4 |
| 2 | 100 | 5 |
+----------+--------+------+
5 rows in set (0.00 sec)
UPDATE: appena notato il che si richiedono i legami di condividere lo stesso rango. Questo è un po 'complicato, ma può essere risolto con ancora più variabili:
UPDATE player
JOIN (SELECT p.playerID,
IF(@lastPoint <> p.points,
@curRank := @curRank + 1,
@curRank) AS rank,
@lastPoint := p.points
FROM player p
JOIN (SELECT @curRank := 0, @lastPoint := 0) r
ORDER BY p.points DESC
) ranks ON (ranks.playerID = player.playerID)
SET player.rank = ranks.rank;
Per un banco di prova, aggiungiamo un altro giocatore con 175 punti:
INSERT INTO player VALUES (6, 175, NULL);
Risultato:
SELECT * FROM player ORDER BY rank;
+----------+--------+------+
| playerID | points | rank |
+----------+--------+------+
| 3 | 250 | 1 |
| 4 | 200 | 2 |
| 5 | 175 | 3 |
| 6 | 175 | 3 |
| 1 | 150 | 4 |
| 2 | 100 | 5 |
+----------+--------+------+
6 rows in set (0.00 sec)
E se si richiede il rango di saltare un posto in caso di parità, è possibile aggiungere un'altra condizione IF
:
UPDATE player
JOIN (SELECT p.playerID,
IF(@lastPoint <> p.points,
@curRank := @curRank + 1,
@curRank) AS rank,
IF(@lastPoint = p.points,
@curRank := @curRank + 1,
@curRank),
@lastPoint := p.points
FROM player p
JOIN (SELECT @curRank := 0, @lastPoint := 0) r
ORDER BY p.points DESC
) ranks ON (ranks.playerID = player.playerID)
SET player.rank = ranks.rank;
Risultato:
SELECT * FROM player ORDER BY rank;
+----------+--------+------+
| playerID | points | rank |
+----------+--------+------+
| 3 | 250 | 1 |
| 4 | 200 | 2 |
| 5 | 175 | 3 |
| 6 | 175 | 3 |
| 1 | 150 | 5 |
| 2 | 100 | 6 |
+----------+--------+------+
6 rows in set (0.00 sec)
. Nota: Si prega di considerare che le query che sto suggerendo potrebbero essere semplificate ulteriormente
Altri suggerimenti
Daniel, avete molto bella soluzione. Tranne uno punto - il caso cravatta. Se legame avviene tra 3 giocatori questo aggiornamento non funziona correttamente. Ho cambiato la vostra soluzione come segue:
UPDATE player
JOIN (SELECT p.playerID,
IF(@lastPoint <> p.points,
@curRank := @curRank + @nextrank,
@curRank) AS rank,
IF(@lastPoint = p.points,
@nextrank := @nextrank + 1,
@nextrank := 1),
@lastPoint := p.points
FROM player p
JOIN (SELECT @curRank := 0, @lastPoint := 0, @nextrank := 1) r
ORDER BY p.points DESC
) ranks ON (ranks.playerID = player.playerID)
SET player.rank = ranks.rank;
EDIT:. La dichiarazione di aggiornamento presentato in precedenza non ha funzionato
Anche se questo non è esattamente quello che stai chiedendo: È possibile generare il rango al volo quando si seleziona:
select p1.playerID, p1.points, (1 + (
select count(playerID)
from Player p2
where p2.points > p1.points
)) as rank
from Player p1
order by points desc
EDIT: Cercando l'istruzione UPDATE una volta di più. Che ne dite di una tabella temporanea:
create temporary table PlayerRank
as select p1.playerID, (1 + (select count(playerID)
from Player p2
where p2.points > p1.points
)) as rank
from Player p1;
update Player p set rank = (select rank from PlayerRank r
where r.playerID = p.playerID);
drop table PlayerRank;
Spero che questo aiuti.
normalizzazione governa , rango deve essere valutata al momento SELECT.