【问题标题】:Update the rank in a MySQL Table更新 MySQL 表中的排名
【发布时间】:2011-02-13 04:53:32
【问题描述】:

我有一个表格播放器的以下表格结构

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

假设 playerID 和 points 具有有效值,我可以根据单个查询中的点数更新所有玩家的排名吗?如果两个人的分数相同,他们应该并列排名。

更新:

我正在使用建议作为本机查询的查询来使用休眠。 Hibernate 不喜欢使用变量,尤其是':'。有谁知道任何解决方法?在这种情况下,是不使用变量还是使用 HQL 来解决 hibernate 的限制?

【问题讨论】:

  • @sammichy:关于你在hibernate上的编辑,你可能想发布一个新问题,因为它会得到更多的关注。

标签: mysql hibernate rank tie


【解决方案1】:

编辑:之前提出的更新语句不起作用。

虽然这不是您所要求的:您可以在选择时即时生成排名:

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

编辑:再次尝试 UPDATE 语句。临时表怎么样:

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;

希望这会有所帮助。

【讨论】:

  • @Tom:不,这行不通。由于子查询中的p1 引用,您将获得You can't specify target table 'p1' for update in FROM clause
  • 感谢丹尼尔的澄清。既然上校 Shrapnel 指出,严格来说,排名应该在选择时计算,让我指出我的子选择应该为此目的工作。
  • @Tom:是的,该子查询将在SELECT 时间工作,但它仍然无法处理关系。 OP 甚至将问题标记为“领带”! :)
  • 我有这个查询的修改版本,更新播放器 g1 set g1.rank = 1 + ((SELECT count(*) from (select * from player) g2 where g2.points > g1.points )) 我想知道是否可以对其进行任何优化。
  • @Daniel:我不明白为什么我的声明不能处理关系。每个得分相同的玩家将获得相同的排名。如果两名玩家并列第 5 名,则下一位玩家将被分配到第 7 名。如果这不是“处理平局”的工作方式,我们对这个术语有不同的理解。
【解决方案2】:

丹尼尔,你有很好的解决方案。除了一点 - 领带盒。如果 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;

【讨论】:

    【解决方案3】:

    一种选择是使用排名变量,例如:

    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)
    

    注意:请考虑我建议的查询可以进一步简化。

    【讨论】:

    • @Daniel,谢谢,这正是我所需要的。谢谢你的链接。
    • 丹尼尔,请看我对我自己的回答的评论。
    【解决方案4】:

    根据Normalization rules,排名应该在选择时进行评估。

    【讨论】:

    • 是的,但这主要是一个定期计算排名的查找表,我不想在每次用户登录时都运行它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    • 2012-08-04
    • 1970-01-01
    • 1970-01-01
    • 2013-09-19
    相关资源
    最近更新 更多