Вопрос

У меня есть следующая структура таблицы для табличного игрока

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

Предполагая, что PlayerID и точки имеют допустимые значения, могу ли я обновить ранг для всех игроков, основанных на количестве точек в одном запросе? Если два человека имеют одинаковое количество точек, они должны связать для ранга.

ОБНОВИТЬ:

Я использую Hibernate, используя запрос, предлагаемый как нативный запрос. Hibernate не любит использовать переменные, особенно «:». Кто-нибудь знает какие-либо обходные пути? Либо не используя переменные или работать по ограничению Hibernate в этом случае с помощью HQL?

Это было полезно?

Решение

Одним из вариантов является использование переменной ранжирования, например:

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;

То JOIN (SELECT @curRank := 0) часть позволяет переменную инициализацию без необходимости отдельного SET команда.

Дальше чтение по этой теме:


Прецедент:

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;

Результат:

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

Для теста, давайте добавим другого игрока с 175 баллами:

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

Результат:

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)

И если вам требуется ранг, чтобы пропустить место в случае галстука, вы можете добавить другой 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;

Результат:

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)

Примечание: пожалуйста, считайте, что запросы, которые я предлагаю, можно упростить дальше.

Другие советы

Даниэль, у вас очень хорошее решение. Кроме одной точки - галстук. Если галстук происходит между 3 игроками, это обновление не работает должным образом. Я изменил ваше решение следующим образом:

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;

Редактировать: оператор обновления, представленный ранее не сработал.

Хотя это не совсем то, что вы просите: вы можете генерировать ранг на лету при выборе:

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

Редактировать: Попробую оператор обновления еще раз. Как насчет временной таблицы:

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;

Надеюсь это поможет.

Согласно с Правила нормализации, Ранг должен быть оценен в Select Time.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top