MySQL 更新排名的查询

MySQL query to update ranking

我有一个 mysql table,其中包含 userid、rating 和 ranking 作为字段。 table 很大(>10K 行)。我正在尝试根据评分更新单个查询中每一行的排名列(评分越高排名越低)。如果 2 个或更多用户的评分相同,则他们具有相同的排名,但下一个排名将跳过那么多用户(如果 2 个用户的排名为 5,则下一个用户将排名 7)。

我正在尝试通过以下查询实现此目的,但不知道哪里出错了。

UPDATE userprofile
            JOIN (SELECT p.userid,
                         IF(@last_rating<> p.rating,
                            @cur_rank := @cur_rank + @next_rank,
                            @cur_rank)  AS ranking,
                         IF(@last_rating= p.rating,
                            @next_rank := @next_rank + 1,
                            @next_rank := 1),
                         @last_rating:= p.rating
                    FROM userprofile p
                    JOIN (SELECT @cur_rank := 0, @last_rating:= -1, @next_rank := 1) r
                   ORDER BY  p.rating DESC
                  ) ranks ON (ranks.userid = userprofile.userid)
        SET userprofile.ranking = ranks.ranking

这是查询前 table 的示例

+--------+--------+-----+------+------------+--------+--------------+---------+---------------+
| userid | played | won | lost |   streak   | rating | ratingchange | ranking | currentgameid |
+--------+--------+-----+------+------------+--------+--------------+---------+---------------+
|      1 |      1 |   0 |    0 | A          |      0 |            0 |       0 |             6 |
|      2 |      1 |   1 |    0 | W          |     50 |           50 |       0 |             2 |
|      7 |     13 |   4 |    5 | AWLLLWWLLW |    108 |           48 |       0 |             0 |
|      8 |      6 |   6 |    0 | WWWWWW     |    198 |           48 |       0 |             0 |
|      9 |      1 |   0 |    0 | A          |      0 |            0 |       0 |             0 |
|     10 |      1 |   1 |    0 | W          |     50 |           50 |       0 |             0 |
|     11 |      7 |   5 |    2 | WWWLWWL    |    142 |          -48 |       0 |             0 |
|     12 |      7 |   1 |    6 | LLLWLLL    |      0 |            0 |       0 |            26 |
|     13 |      5 |   3 |    1 | LWWAW      |     71 |           71 |       0 |             0 |
|     14 |      1 |   1 |    0 | W          |     71 |           71 |       0 |             0 |
|     15 |      0 |   0 |    0 |            |      0 |            0 |       0 |            26 |
+--------+--------+-----+------+------------+--------+--------------+---------+---------------+

并查询后

+--------+--------+-----+------+------------+--------+--------------+---------+---------------+
| userid | played | won | lost |   streak   | rating | ratingchange | ranking | currentgameid |
+--------+--------+-----+------+------------+--------+--------------+---------+---------------+
|      1 |      1 |   0 |    0 | A          |      0 |            0 |       1 |             6 |
|      2 |      1 |   1 |    0 | W          |     50 |           50 |       2 |             2 |
|      7 |     13 |   4 |    5 | AWLLLWWLLW |    108 |           48 |       3 |             0 |
|      8 |      6 |   6 |    0 | WWWWWW     |    198 |           48 |       4 |             0 |
|      9 |      1 |   0 |    0 | A          |      0 |            0 |       5 |             0 |
|     10 |      1 |   1 |    0 | W          |     50 |           50 |       6 |             0 |
|     11 |      7 |   5 |    2 | WWWLWWL    |    142 |          -48 |       7 |             0 |
|     12 |      7 |   1 |    6 | LLLWLLL    |      0 |            0 |       8 |            26 |
|     13 |      5 |   3 |    1 | LWWAW      |     71 |           71 |       9 |             0 |
|     14 |      1 |   1 |    0 | W          |     71 |           71 |       9 |             0 |
|     15 |      0 |   0 |    0 |            |      0 |            0 |      11 |            26 |
+--------+--------+-----+------+------------+--------+--------------+---------+---------------+

如果我使用下面的 select 语句,它会在 ranks.ranking 列中给出预期的结果,但 SET 语句没有按预期工作。

SELECT userprofile.userid, userprofile.rating, ranks.userid, ranks.ranking FROM userprofile
            JOIN (SELECT p.userid,
                         IF(@last_rating<> p.rating,
                            @cur_rank := @cur_rank + @next_rank,
                            @cur_rank)  AS ranking,
                         IF(@last_rating= p.rating,
                            @next_rank := @next_rank + 1,
                            @next_rank := 1),
                         @last_rating:= p.rating
                    FROM userprofile p
                    JOIN (SELECT @cur_rank := 0, @last_rating:= -1, @next_rank := 1) r
                   ORDER BY  p.rating DESC
                  ) ranks ON (ranks.userid = userprofile.userid)

Select 查询的结果

+--------+--------+--------+---------+
| userid | rating | userid | ranking |
+--------+--------+--------+---------+
|      8 |    198 |      8 |       1 |
|     11 |    142 |     11 |       2 |
|      7 |    108 |      7 |       3 |
|     13 |     71 |     13 |       4 |
|     14 |     71 |     14 |       4 |
|      2 |     50 |      2 |       6 |
|     10 |     50 |     10 |       6 |
|      1 |      0 |      1 |       8 |
|      9 |      0 |      9 |       8 |
|     12 |      0 |     12 |       8 |
|     15 |      0 |     15 |       8 |
+--------+--------+--------+---------+

你能创建一个时间 table,然后执行更新吗?

SQL DEMO

CREATE TABLE test as 
SELECT userprofile.*, ranks.ranking  as newRank
FROM userprofile
JOIN (SELECT p.userid,
             IF(@last_rating<> p.rating,
                @cur_rank := @cur_rank + @next_rank,
                @cur_rank)  AS ranking,
             IF(@last_rating= p.rating,
                @next_rank := @next_rank + 1,
                @next_rank := 1),
             @last_rating:= p.rating
      FROM userprofile p
      JOIN (SELECT @cur_rank := 0, @last_rating:= -1, @next_rank := 1) r
      ORDER BY  p.rating DESC
    ) ranks 
ON (ranks.userid = userprofile.userid);

更新

UPDATE userprofile
JOIN test
  ON userprofile.`userid`  = test.`userid`
SET userprofile.ranking = test.newRank;

这是最终对我有用的查询。 选择 Juan 的答案作为接受的答案,因为它是建议这样做的答案。

DROP TABLE IF EXISTS t1;

CREATE TABLE t1 AS
SELECT userprofile.userid, ranks.ranking FROM userprofile
            JOIN (SELECT p.userid,
                         IF(@last_rating<> p.rating,
                            @cur_rank := @cur_rank + @next_rank,
                            @cur_rank)  AS ranking,
                         IF(@last_rating= p.rating,
                            @next_rank := @next_rank + 1,
                            @next_rank := 1),
                         @last_rating:= p.rating
                    FROM userprofile p
                    JOIN (SELECT @cur_rank := 0, @last_rating:= -1, @next_rank := 1) r
                   ORDER BY  p.rating DESC
                  ) ranks ON (ranks.userid = userprofile.userid);

UPDATE userprofile JOIN t1 ON userprofile.userid = t1.userid
SET userprofile.ranking = t1.ranking;

DROP TABLE t1;