Question

J'ai la structure de tableau ci-dessous pour une table lecteur

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

En supposant que le playerID et les points ont des valeurs valides, puis-je mettre à jour le rang pour tous les joueurs en fonction du nombre de points dans une seule requête? Si deux personnes ont le même nombre de points, ils doivent attacher le rang.

Mise à jour:

J'utilise veille prolongée à l'aide de la requête proposée comme une requête native. Mise en veille prolongée ne fonctionne pas comme l'utilisation de variables, en particulier les « : ». Est-ce que quelqu'un sait de solutions de contournement? Soit en utilisant des variables ou pas de travail autour de la limitation de mise en veille prolongée dans ce cas en utilisant HQL?

Était-ce utile?

La solution

Une option consiste à utiliser une variable de classement, comme suit:

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 partie JOIN (SELECT @curRank := 0) permet à l'initialisation des variables, sans nécessiter une commande de SET séparé.

Pour en savoir plus sur ce sujet:


cas de test:

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;

Résultat:

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)

Mise à jour: Il suffit de remarquer le besoin que vous les liens à partager le même rang. Ceci est un peu délicat, mais peut être résolu avec des variables encore plus:

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;

Pour un test, nous allons ajouter un autre joueur avec 175 points:

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

Résultat:

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)

Et si vous avez besoin au rang de sauter une place en cas d'égalité, vous pouvez ajouter une autre condition 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;

Résultat:

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)

Note:. S'il vous plaît considérer que les questions que je propose pourrait simplifier encore

Autres conseils

Daniel, vous avez une solution très agréable. Sauf un point - le cas de cravate. Si égalité se passe entre 3 joueurs cette mise à jour ne fonctionne pas correctement. J'ai changé votre solution comme suit:

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 déclaration de mise à jour présentée précédemment n'a pas fonctionné

Bien que ce n'est pas exactement ce que vous demandez: vous pouvez générer le rang à la volée lors de la sélection:

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: Essayer l'instruction UPDATE une fois de plus. Que diriez-vous d'une table temporaire:

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 this helps.

Selon règles Normalization, doit être évaluée au moment de SELECT rang.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top