题
我有一个表播放器的下表结构
Table Player {
Long playerID;
Long points;
Long rank;
}
假设播放器和点具有有效值,我可以根据单个查询中的点数更新所有播放器的排名吗?如果两个人的分数相同,那么他们应该领先。
更新:
我正在使用建议的查询作为本机查询。 Hibernate不喜欢使用变量,尤其是“:”。有人知道有任何解决方法吗?在这种情况下,不使用变量或在这种情况下使用HQL来解决Hibernate的限制?
解决方案
一种选择是使用排名变量,例如以下内容:
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;
希望这可以帮助。
根据 归一化规则, ,应在选定时间评估等级。
不隶属于 StackOverflow