问题描述
我正在尝试更新玩家的每周排名分数,但是我尝试的任何查询都会超时。表格中大约有10万行。我的桌子 players_weekly_rankings 看起来像这样:
player_id | ranking_points | yearweek | ranking_pos
22 | 1676 | 2020/01 | 1
12 | 1620 | 2020/01 | 2
45 | 1620 | 2020/01 | 2
53 | 1544 | 2020/01 | 4
25 | 1644 | 2020/02 | 1
21 | 1555 | 2020/02 | 2
etc.
所以rank_pos列就是要更新的列。
查询永远不会结束并运行到超时:
update players_weekly_ranking
set ranking_pos = (
select count(distinct ranking_points) + 1
from (SELECT ranking_points,yearweek FROM players_weekly_ranking) w2
where w2.yearweek = players_weekly_ranking.yearweek and w2.ranking_points > players_weekly_ranking.ranking_points
)
并按以下要求进行解释(此测试tebale仅具有2000条记录,但实际表接近100k)
具有多达两千行的内容,它在两分钟内完成,但是超过此数,就会达到超时。
解决方法
我相信这应该一次就可以解决问题(无子查询)
SET @rank = 0;
UPDATE players_weekly_ranking
SET ranking_pos = (@rank := @rank+1)
WHERE yearweek = '2020/01'
ORDER BY ranking_points DESC;
它定义了一个变量@rank
,从0开始,然后通过降序yearweek
遍历特定行ranking_points
的所有行,并为其分配ranking_pos
。 (@rank := @rank+1)
可以在每一行增加变量。
编辑:我假设您只需要更新特定一周的排名,因为过去的分数不会改变
Edit2::以下版本考虑了均分,并且可以在数年的周内更新:
SET @rank = 0; -- rank of the previous row
SET @yearweek = ''; -- yearweek of the previous row
SET @last_score = 0; -- score of the previous row
SET @nb_same_score = 0; -- number of rows "in a row" with same score
UPDATE players_weekly_ranking
SET ranking_pos = IF(
@yearweek != (@yearweek := yearweek),IF( -- if it's a new yearweek
(@last_score := ranking_points) AND (@nb_same_score:=1),(@rank := 1),-- first row always gets ranking_pos = 1
0
),IF( -- if same yearweek
@last_score = (@last_score := ranking_points) AND (@nb_same_score := @nb_same_score + 1),@rank,-- if same score as last row => set same ranking_pos
@rank := @rank + @nb_same_score + (@nb_same_score := 1) -1
)
)
ORDER BY yearweek,ranking_points DESC;
按照年份和周数对每一行进行迭代,这将执行以下操作:
- 如果是新的一周,则第一行(最高分)的排名始终为1。({
@yearweek
采用新一周的值,@last_score
和@nb_same_score
被重置) - 如果与最后一行在同一周,则将
@last_score
与该行的分数进行比较(并更新)。如果它们相等,则@nb_same_score
递增- 如果相等,则该行的排名与上一行相同
- 否则它将获得+
@nb_same_score
的排名。 ((@nb_same_score := 1) -1
只是将@nb_same_score
变量重置为1)
- 用JOIN替换子查询
- 在JOIN'ed表中,使用窗口函数RANK()计算等级。它将完全满足您的需求。
RANK()仅在MySQL 8+ https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html#function_rank
中可用如果您具有MySQL 5. *,请查找不带RANK()函数的解决方法:Rank function in MySQL