我有一个包含3个字段的表,我想根据user_id和game_id对列进行排名。
这是SQL Fiddle: http://sqlfiddle.com/#!9/883e9d/1
我已经有桌子了
node_t *create (char data) { /* simply move allocation/initialization
to a create function */
node_t *node = new node_t; /* allocate new node */
node->data = data; /* set node data */
node->next = NULL;
return node;
}
node_t *insert (char data) {
node_t *node = create (data); /* call create function */
if (!head) /* if 1st node */
head = node;
else { /* otherwise */
iter = head;
while (iter->next) /* traditional iterate to find last */
iter = iter->next;
iter->next = node; /* set new last */
}
return node; /* return node */
}
预期输出:
insert
到目前为止我的努力:
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 |
编辑:(来自OP Comments):排序基于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 |
game_detail的顺序
答案 0 :(得分:6)
在Derived Table(FROM
子句中的子查询)中,我们对数据进行排序,以使所有具有相同user_id
值的行都聚在一起,并根据{ {1}}降序。
现在,我们使用此结果集并使用条件game_detail
表达式来评估行编号。就像循环技术(我们在应用程序代码中使用的,例如:PHP)一样。我们将前一行的值存储在用户定义的变量中,然后对照前一行检查当前行的值。最终,我们将相应地分配行号。
编辑::基于MySQL docs和@Gordon Linoff的观察:
涉及用户变量的表达式的求值顺序为 未定义。例如,不能保证SELECT @a,@a:= @ a + 1 首先评估@a,然后执行分配。
我们需要评估行号,并将CASE..WHEN
的值分配给同一表达式内的user_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
最近发现的来自MySQL Docs的有趣笔记:
以前的MySQL版本可以为一个值分配一个值 SET以外的语句中的用户变量。此功能是 MySQL 8.0支持向后兼容,但要遵守 在将来的MySQL版本中删除。
另外,由于有一个SO成员,MySQL团队还访问了此博客:https://mysqlserverteam.com/row-numbering-ranking-how-to-use-less-user-variables-in-mysql-queries/
一般的观察结果是,在同一查询块中使用| 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 |
评估用户变量不会保证值始终正确。因此,MySQL优化器 可能会出现,并更改我们的假定的评估顺序。
解决此问题的最佳方法是升级到MySQL 8+并利用Row_Number()
功能:
模式(MySQL v8.0)
ORDER BY
结果
SELECT user_id,
game_id,
game_detail,
ROW_NUMBER() OVER (PARTITION BY user_id
ORDER BY game_detail DESC) AS user_game_rank
FROM game_logs
ORDER BY user_id, user_game_rank;
答案 1 :(得分: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不保证表达式的求值顺序)。
答案 2 :(得分:3)
SELECT user_id, game_id, game_detail,
CASE WHEN user_id = @lastUserId
THEN @rank := @rank + 1
ELSE @rank := 1
END As user_game_rank,
@lastUserId := user_id
FROM game_logs
cross join (select @rank := 0, @lastUserId := 0) r
order by user_id, game_detail desc
答案 3 :(得分:1)
您可以使用一个非常简单的相关子查询:
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
它比用户变量慢,但可靠得多。只需要一个JOIN即可打破它们。