根据组和分数计算用户的排名

Posted

技术标签:

【中文标题】根据组和分数计算用户的排名【英文标题】:Calculate Rank for users based on groups and score 【发布时间】:2020-01-22 07:11:10 【问题描述】:

我看到有很多类似的问题,但我已经耗尽了我找到的所有解决方案。它们都没有像我预期的那样工作。

我的场景如下:

    带有用户分数的表格(其他数据被剥离,不相关):
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| user_id      | int(11)      | NO   | MUL | NULL    |                |
| score        | int(11)      | NO   |     | NULL    |                |
| rank         | int(11)      | NO   |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+
    带有用户的表格(其他数据被剥离,不相关):
+-----------------+--------------+------+-----+---------+----------------+
| Field           | Type         | Null | Key | Default | Extra          |
+-----------------+--------------+------+-----+---------+----------------+
| id              | int(11)      | NO   | PRI | NULL    | auto_increment |
| group_id        | int(11)      | NO   | MUL | NULL    |                |
+-----------------+--------------+------+-----+---------+----------------+
    带有组的表格(其他数据被剥离,不相关):
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| name         | varchar(100) | NO   |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+

要求是:

    根据 100 位用户的分数计算排名,不分组,并相应地增加排名。 基于组的 100 位用户的分数计算排名(每个组的每组结果都应该有自己的排名),并相应地增加排名。

想要的结果是:

+----+------------+----------+------+
| id |    user_id |    score | rank |
+----+------------+----------+------+
|  2 |         29 |       10 |    1 |
|  5 |         32 |        3 |    2 |
|  6 |         33 |        2 |    3 |
|  7 |         34 |        0 |    4 |
|  9 |         39 |        0 |    5 |
| 11 |         41 |        0 |    6 |
| 15 |         47 |        0 |    7 |
| 18 |         51 |        0 |    8 |
+----+------------+----------+------+

基本上用户应该是按分数来排名的,排名应该一直持续到100,即使他们有0。

经过测试的解决方案:

Calculate rank of the users based on their max score using mysql Rank users in mysql by their total points across multiple rows Rank users in mysql by their points How can I calculate rank based on highest Score and lowest Minutes in MySql Server

还有更多,但结果是一样的:

+----+------------+----------+------+
| id |    user_id |    score | rank |
+----+------------+----------+------+
|  2 |         29 |       10 |    1 |
|  5 |         32 |        3 |    2 |
|  6 |         33 |        2 |    3 |
|  7 |         34 |        0 |    4 |
|  9 |         39 |        0 |    4 | <-- NOT OK (should be 5)
| 11 |         41 |        0 |    4 | <-- NOT OK (should be 6)
+----+------------+----------+------+

请注意,一些得分为 0 的用户获得了排名……“4”,然后就停止了。我试图解决这个问题很长时间,但没有成功。

甚至还没有正确计算每个组。

使用的最新查询:

SELECT
    T1.id,
    T1.`user_id`,
    T1.`score`,
    T2.rank
    FROM
    `user_scores` T1
    LEFT JOIN (
    SELECT
        `score`,
        (@v_id := @v_Id + 1) AS rank
    FROM
        (
            SELECT DISTINCT
                `score`
            FROM
                `user_scores`
            ORDER BY
                `score` DESC
        ) t,
        (SELECT @v_id := 0) r
    ) T2 ON T1.`score` = T2.`score`   
    ORDER BY
    `score` DESC
    LIMIT 100

我希望我的问题足够清楚,并且有人可以为我提供解决方案/解释。

谢谢

【问题讨论】:

@Strawberry 你在说什么?我提供了一切,从表格到查询。 我只能再次向您推荐链接问题中已接受的答案 【参考方案1】:

试试这个解决方案。

SELECT
    T1.id,
    T1.`user_id`,
    T1.`score`,
    (@v_id := @v_Id + 1) AS rank
    FROM
    `user_scores` T1,
    (SELECT @v_id := 0) r
    LEFT JOIN (
    SELECT
        `score`,
    FROM
        (
            SELECT
                `score`
            FROM
                `user_scores`
            ORDER BY
                `score` DESC
        ) t,
    ) T2 ON T1.`score` = T2.`score`   
    ORDER BY
    `score` DESC
    LIMIT 100

第二种方法:

SET @v_id := 0  //first execute this query

/* then execute this query */
SELECT
T1.id,
T1.`user_id`,
T1.`score`,
(@v_id := @v_Id + 1) AS rank
FROM
`user_scores` T1,
LEFT JOIN (
SELECT
    `score`,
FROM
    (
        SELECT
            `score`
        FROM
            `user_scores`
        ORDER BY
            `score` DESC
    ) t,
) T2 ON T1.`score` = T2.`score`   
ORDER BY
`score` DESC
LIMIT 100

【讨论】:

此 sql 查询未经测试。这只是一个想法。【参考方案2】:
    根据 100 个用户的分数计算排名,不分组,并相应增加排名
SET @v_id:=0;
SELECT T1.id,
       T1.`user_id`,
       T1.`score`,
       (@v_id := @v_Id + 1) AS rank
FROM `user_scores` T1
ORDER BY `score` DESC
LIMIT 100;
    基于组的 100 位用户的分数计算排名(每个组的每组结果都应该有自己的排名),并相应地增加排名。
SET @v_id:=0;


SET @prev:="";


SELECT id,
       user_id,
       group_id,
       score,
       rank
FROM
  (SELECT T.*,
          @prev AS prev,
          (@v_id := (CASE WHEN(@prev = ""
                               OR @prev = T.`group_id`) THEN @v_id + 1
                         ELSE
                                (SELECT @prev := 1)
                     END)) AS rank, @prev := T.`group_id`
   FROM
     (SELECT T1.id,
             T1.`user_id`,
             T1.`score`,
             T2.`group_id`
      FROM `user_scores` T1
      LEFT JOIN `users` T2 ON T2.`id` = T1.`user_id`
      ORDER BY `group_id` DESC, `score` DESC
      LIMIT 100) T) TT;

对于数字 2,我刚刚保存了之前的 group_id 值并将其与当前的 group_id 进行比较,以便知道何时重置排名。

【讨论】:

有趣,我现在就测试一下。每组怎么样? 我更新了我的要求,因为我不想再忽略那些为 0 的了。 @NorbertBoros 你可以在select之外使用SET @v_id:=0;吗? 我可以使用任何东西,只要我得到结果就可以了。我阅读了关于 ROW_NUMBER 的内容,它似乎可以满足我的需要,但需要进行测试。【参考方案3】:

也许您希望您的查询如下:

SELECT MIN(id) id, user_id, score, rank FROM user_scores GROUP BY rank LIMIT 100

【讨论】:

以上是关于根据组和分数计算用户的排名的主要内容,如果未能解决你的问题,请参考以下文章

Java List集合计算排名,相同分数名次一样

如何结合这两个查询来计算排名变化?

SlopOne推荐算法(附Python源码)

mysql中的用户按他们的分数排名

Laravel如何按积分获取当前用户排名?

增加对每个学生的分数进行排名的功能