【问题标题】:Finding a users maximum score and the associated details查找用户的最高分数和相关的详细信息
【发布时间】:2010-03-28 22:51:44
【问题描述】:

我有一个表格,用户在其中存储分数和有关所述分数的其他信息(例如分数注释或所用时间等)。我想要一个 mysql 查询,它可以找到每个用户的个人最好成绩以及相关的笔记和时间等。

我尝试使用的是这样的:

SELECT *, MAX(score) FROM table GROUP BY (user)

这样做的问题是,虽然您可以从该查询 [MAX(score)] 中获得用户的个人最佳成绩,但返回的音符和时间等与最高分数无关,而是一个不同的分数(特别是包含的分数)在 *)。有没有办法可以编写一个查询来选择我想要的内容?还是我必须在 PhP 中手动完成?

【问题讨论】:

  • 如果一个用户有两个相同的分数并且都是最大值,你想要两个都返回还是只返回其中一个。如果是后者,是哪一个?

标签: mysql grouping max


【解决方案1】:

我假设您只希望每位玩家获得一个结果,即使他们不止一次获得相同的最高分数。我还假设您希望每个玩家第一次在有重复的情况下获得个人最好成绩。

有几种方法可以做到这一点。这是一种特定于 MySQL 的方法:

SELECT user, scoredate, score, notes FROM (
    SELECT *, @prev <> user AS is_best, @prev := user
    FROM table1, (SELECT @prev := -1) AS vars
    ORDER BY user, score DESC, scoredate
) AS T1
WHERE is_best

这是使用普通 SQL 的更通用的方式:

SELECT T3.* FROM table1 AS T3
JOIN (
    SELECT T1.user, T1.score, MIN(scoredate) AS scoredate
    FROM table1 AS T1
    JOIN (SELECT user, MAX(score) AS score FROM table1 GROUP BY user) AS T2
    ON T1.user = T2.user AND T1.score = T2.score
    GROUP BY T1.user
) AS T4
ON T3.user = T4.user AND T3.score = T4.score AND T3.scoredate = T4.scoredate

结果:

1, '2010-01-01 17:00:00', 50, 'Much better'
2, '2010-01-01 14:00:00', 100, 'Perfect score'

我用来测试这个的测试数据:

CREATE TABLE table1 (user INT NOT NULL, scoredate DATETIME NOT NULL, score INT NOT NULL, notes NVARCHAR(100) NOT NULL);
INSERT INTO table1 (user, scoredate, score, notes) VALUES
(1, '2010-01-01 12:00:00', 10, 'First attempt'),
(1, '2010-01-01 17:00:00', 50, 'Much better'),
(1, '2010-01-01 22:00:00', 30, 'Time for bed'),
(2, '2010-01-01 14:00:00', 100, 'Perfect score'),
(2, '2010-01-01 16:00:00', 100, 'This is too easy');

【讨论】:

    【解决方案2】:

    您可以加入子查询,如下例所示:

    SELECT   t.*,
             sub_t.max_score
    FROM     table t
    JOIN     (SELECT   MAX(score) as max_score,
                       user
              FROM     table
              GROUP BY user) sub_t ON (sub_t.user = t.user AND
                                       sub_t.max_score = t.score);
    

    上面的查询可以解释如下。开头是:

    SELECT t.* FROM table t;
    

    ... 这本身显然会列出表格的所有内容。目标是只保留代表特定用户最高分数的行。因此,如果我们有以下数据:

    +------------------------+
    | user | score |  notes  | 
    +------+-------+---------+
    |    1 |    10 |  note a |
    |    1 |    15 |  note b |    
    |    1 |    20 |  note c |
    |    2 |     8 |  note d |
    |    2 |    12 |  note e |
    |    2 |     5 |  note f |
    +------+-------+---------+
    

    ...我们本来希望只保留“note c”和“note e”行。

    要找到我们想要保留的行,我们可以简单地使用:

    SELECT MAX(score), user FROM table GROUP BY user;
    

    请注意,我们无法从上述查询中获取 notes 属性,因为您已经注意到,对于未与 aggregate function 聚合的字段(例如 MAX() 或不属于 @ 的一部分),您将无法获得预期的结果987654330@ 子句。要进一步阅读此主题,您可能需要查看:

    现在我们只需要保留第一个查询中与第二个查询匹配的行。我们可以使用INNER JOIN

    ...
    JOIN     (SELECT   MAX(score) as max_score,
                       user
              FROM     table
              GROUP BY user) sub_t ON (sub_t.user = t.user AND
                                       sub_t.max_score = t.score);
    

    子查询被命名为sub_t。它是个人最好成绩的所有用户的集合。 JOINON 子句将限制应用于相关字段。请记住,我们只想保留属于此子查询的行。

    【讨论】:

    • 非常感谢,这确实有效。 :) 你有没有机会解释这个子查询业务发生了什么,或者给我指点教程之类的?我以前从未见过这种情况。如果没有,谢谢你,我会在早上解决这个问题!
    • 这可行,但请注意,如果同一玩家多次获得相同分数,它可能会返回多个结果。
    • @Mark:你是对的。但是,如果这不是用户需要的,并且有一些逻辑可以选择个人最佳分数之一(例如,在平局的情况下选择最新分数),也有一些解决方案.
    【解决方案3】:
    SELECT *
    FROM table t
    ORDER BY t.score DESC
    GROUP BY t.user
    LIMIT 1
    

    旁注:最好指定字段而不是使用SELECT *

    【讨论】:

    • 我认为这不是他想要的。他想为每个用户占一行。
    猜你喜欢
    • 1970-01-01
    • 2021-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-28
    • 2020-12-20
    • 2020-01-09
    • 2015-11-02
    相关资源
    最近更新 更多