假设我有下表:
user_id | category_id | points
-------------------------------
1 | 1 | 4
2 | 1 | 2
2 | 1 | 5
1 | 2 | 3
2 | 2 | 2
1 | 3 | 1
2 | 3 | 4
1 | 3 | 8
有人可以帮我构建一个查询来返回每个类别的用户排名 - 如下所示:
user_id | category_id | total_points | rank
-------------------------------------------
1 | 1 | 4 | 2
1 | 2 | 3 | 1
1 | 3 | 9 | 1
2 | 1 | 7 | 1
2 | 2 | 2 | 2
2 | 3 | 4 | 2
答案 0 :(得分:3)
首先,您需要获得每个类别的总积分。然后你需要枚举它们。在MySQL中,这很容易用变量完成:
SELECT user_id, category_id, points,
(@rn := if(@cat = category_id, @rn + 1,
if(@cat := category_id, 1, 1)
)
) as rank
FROM (SELECT u.user_id, u.category_id, SUM(u.points) as points
FROM users u
GROUP BY u.user_id, u.category_id
) g cross join
(SELEct @user := -1, @cat := -1, @rn := 0) vars
ORDER BY category_id, points desc;
答案 1 :(得分:1)
您希望获得每个唯一SUM
的{{1}}分数:
category_id
答案 2 :(得分:0)
MySQL不像其他数据库(Oracle,SQL Server)那样具有分析功能,这对于返回这样的结果非常方便。
前三列很简单,只有GROUP BY user_id, category_id
和SUM(points)
。
返回rank
列是一个问题。除了在客户端上执行此操作外,如果需要在SQL语句中执行此操作,则可以使用MySQL用户定义的变量。
SELECT @rank := IF(@prev_category = r.category_id, @rank+1, 1) AS rank
, @prev_category := r.category_id AS category_id
, r.user_id
, r.total_points
FROM (SELECT @prev_category := NULL, @rank := 1) i
CROSS
JOIN ( SELECT s.category_id, s.user_id, SUM(s.points) AS total_points
FROM users s
GROUP BY s.category_id, s.user_id
ORDER BY s.category_id, total_points DESC
) r
ORDER BY r.category_id, r.total_points DESC, r.user_id DESC
内联视图别名为i
的目的是初始化用户定义的变量。别名为r
的内联视图返回每个(user_id,category_id)的total_points。
"技巧"是将前一行的category_id值与当前行的值进行比较;如果它们匹配,我们将等级增加1.如果它是"新的"类别,我们将等级重置为1.注意,这仅适用于行按类别排序,然后按total_points降序,因此我们需要ORDER BY子句。另请注意,SELECT列表中表达式的顺序很重要;我们需要在用当前值覆盖之前对之前的值进行比较,因此对@prev_category的赋值必须遵循条件测试。
另请注意,如果两个用户在一个类别中具有相同的total_points,则他们将获得排名的不同值...上面的查询不会为平局提供相同的排名。 (可以修改查询以执行此操作,但我们还需要保留前一行的total_points,因此我们可以与当前行进行比较。
另请注意,此语法特定于MySQL,并且这种行为不保证。
如果您需要特定顺序中的列和/或特定顺序的行(为了获得指定的确切结果集),我们需要将上面的查询包装为内联视图。
SELECT t.user_id
, t.category_id
, t.total_points
, t.rank
FROM (
SELECT @rank := IF(@prev_category = r.category_id, @rank+1, 1) AS rank
, @prev_category := r.category_id AS category_id
, r.user_id
, r.total_points
FROM (SELECT @prev_categor := NULL, @rank := 1) i
CROSS
JOIN ( SELECT s.category_id, s.user_id, SUM(s.points) AS total_points
FROM users s
GROUP BY s.category_id, s.user_id
ORDER BY s.category_id, total_points DESC
) r
ORDER BY r.category_id, r.total_points DESC, r.user_id DESC
) t
ORDER BY t.user_id, t.category_id
注意:我没有设置SQL Fiddle演示。我给出了一个只经过桌面检查的示例查询。