Pregunta

Tengo la siguiente estructura de la tabla para una tabla jugador

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

Si se asume que el playerId y los puntos tienen valores válidos, puedo actualizar el rango para todos los jugadores basándose en el número de puntos en una sola consulta? Si dos personas tienen el mismo número de puntos, se debe atar para el rango.

ACTUALIZACIÓN:

Estoy usando Hibernate utilizando la consulta sugerido como una consulta nativa. Hibernate no le gusta el uso de variables, especialmente el ':'. ¿Alguien sabe de alguna solución? Ya sea por no utilizar las variables o trabajar cerca de la limitación de hibernación en este caso mediante el uso de HQL?

¿Fue útil?

Solución

Una opción es utilizar una variable de ranking, tales como los siguientes:

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) permite que la variable de inicialización sin necesidad de un mando separado SET.

Para leer más sobre este tema:


caso de prueba:

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;

Resultados:

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)

ACTUALIZACIÓN: Sólo se dio cuenta de la que necesita lazos de compartir el mismo rango. Esto es un poco complicado, pero se puede solucionar con aún más variables:

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;

Para un caso de prueba, vamos a añadir otro jugador con 175 puntos:

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

Resultados:

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)

Y si usted requiere el rango de saltar un lugar en caso de empate, se puede añadir otra condición 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;

Resultados:

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:. Por favor, considere que las consultas que estoy sugiriendo podrían simplificarse aún más

Otros consejos

Daniel, que tiene muy buena solución. Excepto un punto - el caso lazo. Si sucede lazo entre 3 jugadores esta actualización no funciona correctamente. He cambiado su solución como sigue:

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 instrucción de actualización presentada anteriormente no funcionó

A pesar de que esto no es exactamente lo que está pidiendo: Puede generar el rango sobre la marcha cuando la selección:

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: Tratar la instrucción UPDATE una vez más. ¿Qué tal una tabla temporal:

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;

Espero que esto ayude.

Según Normalización gobierna , el rango debe ser evaluada en el tiempo de SELECT.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top