Update the rank in a MySQL Table

后端 未结 4 861
梦如初夏
梦如初夏 2020-12-08 06:14

I have the following table structure for a table Player

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

Assuming that th

4条回答
  •  轮回少年
    2020-12-08 06:18

    One option is to use a ranking variable, such as the following:

    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;
    

    The JOIN (SELECT @curRank := 0) part allows the variable initialization without requiring a separate SET command.

    Further reading on this topic:

    • SQL: Ranking without self join
    • Stack Overflow: Create a Cumulative Sum Column in MySQL

    Test Case:

    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;
    

    Result:

    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: Just noticed the that you require ties to share the same rank. This is a bit tricky, but can be solved with even more 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;
    

    For a test case, let's add another player with 175 points:

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

    Result:

    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)
    

    And if you require the rank to skip a place in case of a tie, you can add another IF condition:

    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;
    

    Result:

    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: Please consider that the queries I am suggesting could be simplified further.

提交回复
热议问题