基于MySQL中的多个列确定排名
我有一个有3个字段的表,我想根据用户id和游戏id对列进行排名 下面是SQL Fiddle: 我已经有了这张表:基于MySQL中的多个列确定排名,mysql,sql,sql-rank,Mysql,Sql,Sql Rank,我有一个有3个字段的表,我想根据用户id和游戏id对列进行排名 下面是SQL Fiddle: 我已经有了这张表: user_id | game_id | game_detial_sum | --------|---------|--------------------| 6 | 10 | 1000 | 6 | 11 | 260 | 7 | 10 |
user_id | game_id | game_detial_sum |
--------|---------|--------------------|
6 | 10 | 1000 |
6 | 11 | 260 |
7 | 10 | 1200 |
7 | 11 | 500 |
7 | 12 | 360 |
7 | 13 | 50 |
预期产出:
user_id | game_id | game_detial_sum | user_game_rank |
--------|---------|--------------------|------------------|
6 | 10 | 1000 | 1 |
6 | 11 | 260 | 2 |
7 | 10 | 1200 | 1 |
7 | 11 | 500 | 2 |
7 | 12 | 360 | 3 |
7 | 13 | 50 | 4 |
我迄今为止的努力:
SET @s := 0;
SELECT user_id,game_id,game_detail,
CASE WHEN user_id = user_id THEN (@s:=@s+1)
ELSE @s = 0
END As user_game_rank
FROM game_logs
编辑:(来自OP):排序基于游戏详细信息的降序
游戏顺序(详情)
在(FROM
子句内的子查询)中,我们对数据进行排序,使所有具有相同user\u id
值的行聚集在一起,并根据game\u detail
按降序对它们进行进一步排序
现在,我们使用这个结果集并使用条件CASE..WHEN
表达式来计算行编号。这将类似于循环技术(我们在应用程序代码中使用,例如:PHP)。我们将把前一行的值存储在用户定义的变量中,然后对照前一行检查当前行的值。最后,我们将相应地分配行号
编辑:基于MySQL和@Gordon Linoff的观察:
涉及用户变量的表达式的求值顺序为
未定义。例如,不能保证选择@a,@a:=@a+1
首先计算@a,然后执行赋值
我们需要计算行号,并将用户id
值分配给同一表达式中的@u
变量
SET @r := 0, @u := 0;
SELECT
@r := CASE WHEN @u = dt.user_id
THEN @r + 1
WHEN @u := dt.user_id /* Notice := instead of = */
THEN 1
END AS user_game_rank,
dt.user_id,
dt.game_detail,
dt.game_id
FROM
( SELECT user_id, game_id, game_detail
FROM game_logs
ORDER BY user_id, game_detail DESC
) AS dt
结果
| user_game_rank | user_id | game_detail | game_id |
| -------------- | ------- | ----------- | ------- |
| 1 | 6 | 260 | 11 |
| 2 | 6 | 100 | 10 |
| 1 | 7 | 1200 | 10 |
| 2 | 7 | 500 | 11 |
| 3 | 7 | 260 | 12 |
| 4 | 7 | 50 | 13 |
| user_id | game_id | game_detail | user_game_rank |
| ------- | ------- | ----------- | -------------- |
| 6 | 11 | 260 | 1 |
| 6 | 10 | 100 | 2 |
| 7 | 10 | 1200 | 1 |
| 7 | 11 | 500 | 2 |
| 7 | 12 | 260 | 3 |
| 7 | 13 | 50 | 4 |
MySQL的一条有趣的注释,我最近发现:
MySQL的早期版本可以为
语句中的用户变量,而不是SET。此功能是
MySQL 8.0支持向后兼容,但受
在MySQL的未来版本中删除
另外,感谢SO的一位同事,MySQL团队发现了这个博客:
一般的观察结果是,在同一查询块中使用
ORDER BY对用户变量进行求值,并不能确保值总是正确的。随着时间的推移,MySQL优化器可能会就位并更改我们假定的评估顺序
解决此问题的最佳方法是升级到MySQL 8+并利用以下功能:
模式(MySQL v8.0)
结果
| user_game_rank | user_id | game_detail | game_id |
| -------------- | ------- | ----------- | ------- |
| 1 | 6 | 260 | 11 |
| 2 | 6 | 100 | 10 |
| 1 | 7 | 1200 | 10 |
| 2 | 7 | 500 | 11 |
| 3 | 7 | 260 | 12 |
| 4 | 7 | 50 | 13 |
| user_id | game_id | game_detail | user_game_rank |
| ------- | ------- | ----------- | -------------- |
| 6 | 11 | 260 | 1 |
| 6 | 10 | 100 | 2 |
| 7 | 10 | 1200 | 1 |
| 7 | 11 | 500 | 2 |
| 7 | 12 | 260 | 3 |
| 7 | 13 | 50 | 4 |
MySQL 8.0版之前的最佳解决方案如下:
select gl.*,
(@rn := if(@lastUserId = user_id, @rn + 1,
if(@lastUserId := user_id, 1, 1)
)
) as user_game_rank
from (select gl.*
from game_logs gl
order by gl.user_id, gl.game_detail desc
) gl cross join
(select @rn := 0, @lastUserId := 0) params;
排序是在子查询中完成的。从MySQL 5.7开始,这是必需的。变量赋值都在一个表达式中,因此表达式的不同求值顺序无关紧要(MySQL也不保证表达式的求值顺序)。您可以使用一个非常简单的相关子查询:
SELECT *, (
SELECT COUNT(DISTINCT game_detail) + 1
FROM game_logs AS x
WHERE user_id = t.user_id AND game_detail > t.game_detail
) AS user_game_rank
FROM game_logs AS t
ORDER BY user_id, user_game_rank
它比用户变量更慢但更可靠。只需一个连接就可以打破它们。是基于
game\u id
的升序或game\u detail
的降序的游戏顺序_detail@GordonLinoffMySQL团队的特定博客可能会有所帮助:2)这个答案是错误的,因为它没有根据游戏详细信息
按降序排列。这似乎是可行的,因为(不幸的是)OP的样本数据本身是不够的(已经排序)。第三,afaik,这个答案和另一个答案(我的答案)中的用户变量的计算是在两个不同的表达式中进行的(用逗号分隔)。如果具体的差异可以得到解决,我会很高兴的showcased@MadhurBhaiya:我添加了一个orderby
@juergend,我也会要求你读一次这个博客:基本上MySQL不保证评估是在orderby
之前还是之后进行(由于自身的优化)@juergend我在评论另一个问题时,与Nick聊过这类问题,以及某些场景中的意外行为等。你可能对同样的东西感兴趣:@MadhurBhaiya。你说得对。这与您的错误相同。@lastUserId:=user\u id
仍然可以在if()
表达式之前求值。肯定是if(user_id:=@lastUserId
中涉及了一些技巧,但无法理解。这是怎么回事?一些解释会很方便。@MadhurBhaiya…那一行是个错误,已被删除。它没有在计算中使用。现在有意义了。+1