Frage

Ich habe die folgende Tabellenstruktur für eine Tabelle Spieler

Table Player {  
Long playerID;  
Long points;  
Long rank;  
}

Unter der Annahme, dass die playerid und die Punkte gültige Werte haben, kann ich den Rang für alle Spieler aktualisieren basierend auf der Anzahl von Punkten in einer einzigen Abfrage? Wenn zwei Menschen die gleiche Anzahl von Punkten haben, sollten sie für den Rang binden.

UPDATE:

Ich verwende Hibernate mit der Abfrage als native Abfrage vorgeschlagen. Hibernate nicht wie Variablen, vor allem der ‚:‘. Kennt jemand von irgendwelchen Abhilfen? Entweder durch nicht-Variablen oder arbeiten rund um Hibernate Einschränkung in diesem Fall durch die Verwendung HQL?

War es hilfreich?

Lösung

Eine Option ist ein Ranking Variable zu verwenden, wie die folgenden:

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;

Der JOIN (SELECT @curRank := 0) Teil ermöglicht die variable Initialisierung ohne einen separaten SET Befehl erforderlich ist.

Weiterführende Literatur zu diesem Thema:


Testfall:

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;

Ergebnis:

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: habe gerade bemerkt, dass Sie die Beziehungen erfordern den gleichen Rang zu teilen. Das ist ein bisschen schwierig, aber mit noch mehr Variablen gelöst werden:

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;

Für einen Testfall, lassen Sie uns einen anderen Spieler mit 175 Punkten hinzufügen:

INSERT INTO player VALUES (6, 175, NULL);

Ergebnis:

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)

Und wenn Sie den Rang benötigen einen Platz im Falle eines Unentschiedens zu überspringen, können Sie eine weitere IF Bedingung hinzu:

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;

Ergebnis:

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)

. Hinweis: Bitte beachten Sie, dass die Anfragen konnte ich will damit weiter vereinfacht werden

Andere Tipps

Daniel, Sie haben sehr schöne Lösung. Außer einem Punkt - die Krawatte Fall. Wenn Bindung zwischen 3 Spielern passiert, ist dieses Update nicht richtig funktionieren. Ich änderte Ihre Lösung wie folgt:

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:. Die Update-Anweisung vorgestellt früher funktioniert nicht

Das ist zwar nicht genau, was Sie fordern: Sie können den Rang on the fly erzeugen bei der Auswahl:

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: Der Versuch, die UPDATE-Anweisung noch einmal. Wie wäre es eine temporäre Tabelle:

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;

Hope, das hilft.

Nach Normalisierungsregeln , Rang sollte bei SELECT Zeit ausgewertet werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top