MySQL:获得用户的最高分

时间:2009-11-04 05:12:22

标签: sql mysql

我有下表(高分),

id      gameid      userid      name      score      date
1       38          2345        A         100        2009-07-23 16:45:01
2       39          2345        A         500        2009-07-20 16:45:01
3       31          2345        A         100        2009-07-20 16:45:01
4       38          2345        A         200        2009-10-20 16:45:01
5       38          2345        A         50         2009-07-20 16:45:01
6       32          2345        A         120        2009-07-20 16:45:01
7       32          2345        A         100        2009-07-20 16:45:01

现在在上述结构中,用户可以多次玩游戏,但我想显示特定用户的“游戏玩法”。所以在游戏部分我不能展示多个游戏。所以这个概念应该就像用户玩了3次游戏一样,然后才能显示出得分最高的游戏。

我想要结果数据:

id      gameid      userid      name      score      date
2       39          2345        A         500        2009-07-20 16:45:01
3       31          2345        A         100        2009-07-20 16:45:01
4       38          2345        A         200        2009-10-20 16:45:01
6       32          2345        A         120        2009-07-20 16:45:01

我尝试了以下查询,但它没有给我正确的结果:

SELECT id, 
       gameid, 
       userid, 
       date, 
       MAX(score) AS score 
  FROM highscores
 WHERE userid='2345' 
GROUP BY gameid 

请告诉我这是什么问题?

由于

3 个答案:

答案 0 :(得分:7)

要求有点模糊/混乱,但是这样的事情满足了需要吗? (故意添加可能感兴趣的各种聚合)。

SELECT gameid, 
       MIN(date) AS FirstTime, 
       MAX(date) AS LastTime,
       MAX(score) AS TOPscore.
       COUNT(*)  AS NbOfTimesPlayed 
FROM highscores
WHERE userid='2345' 
GROUP BY gameid
-- ORDER BY COUNT(*) DESC -- for ex. to have games played most at top

修改:关于将id列添加到SELECT列表的新问题
简短的回答是:“不,不能添加id,不能在这个特定的构造中添加”。 (进一步阅读以了解原因)但是,如果目的是使得得分最高的游戏的id,则可以使用子查询修改查询以实现该目标。

正如Alex M在此页面上所解释的那样,SELECT列表中引用的所有列名称以及未在聚合函数(MAX,MIN,AVG,COUNT等)的上下文中使用的列名称必须包含在ORDER BY子句。这种SQL语言规则的原因很简单,就是在收集结果列表的信息时,SQL可能会遇到这样一个列的多个值(列在SELECT但不是GROUP BY中),然后就不知道如何处理它了; SQL标准规定了错误信息,而不是做任何事情 - 可能有用但可能很愚蠢 - 这样,用户可以修改查询并明确表达他/她的目标。

在我们的特定情况下,我们可以在SELECT中添加id并将其添加到GROUP BY列表中,但是这样做,聚合发生的分组将是不同的:结果列表将包含尽可能多的行因为我们有id + gameid组合,每一行的聚合值将仅基于表中id和gameid具有相应值的记录(假设id是表中的PK,我们得到一个每个聚合行,使MAX()等无意义。)

使用最高分包括与游戏相对应的id(以及可能的其他列)的方法是使用子查询。这个想法是子查询选择具有TOP得分的游戏(在给定的组中),并且主要查询选择此行的任何列,即使在子查询的组中没有(不能)fieds -by构造。顺便说一句,请在此页面上给予赞美,以便首先显示此类查询。

SELECT H.id, 
       H.gameid, 
       H.userid, 
       H.name,
       H.score,
       H.date        
FROM highscores H
JOIN (
  SELECT M.gameid, hs.userid, MAX(hs.score) MaxScoreByGameUser
  FROM highscores H2
  GROUP BY H2.gameid, H2.userid
) AS M  
   ON M.gameid = H.gameid 
      AND M.userid = H.userid
      AND M.MaxScoreByGameUser = H.score
WHERE H.userid='2345' 

关于上述查询的一些重要评论

  • 重复:如果用户玩过几个达到相同高分的游戏,则查询会产生很多行。
  • 子查询的GROUP BY可能需要针对查询的不同用途进行更改。如果不是在每个用户的基础上搜索游戏的高分,我们想要绝对的高分,我们需要从GROUP BY中排除userid(这就是为什么我用一个长而明确的名字命名MAX的别名)
  • 为了提高效率,可以在子查询的[now absent] WHERE子句中添加userid ='2345'(除非MySQL的优化器非常智能,目前所有游戏+用户组合的所有高分都得到计算,我们只需要用户'2345');向下重复;解;变量

有几种方法可以解决上面提到的问题,但这些方法似乎超出了关于GROUP BY结构的[现在相当长篇]解释的范围。

答案 1 :(得分:2)

SELECT中的每个字段(当存在GROUP BY子句时)必须是GROUP BY子句中的一个字段,或者是MAX,SUM,AVG等组函数。在代码中,userid在技术上违反了它,但是以一种非常无害的方式(你可以使你的代码在技术上符合SQL GROUP BY gameid, userid的标准;字段iddate受到更严重的违规行为 - 在一个GROUP BY集合中会有许多ID和日期,并且您没有告诉如何从该集合中创建单个值(MySQL选择一个或多或少的随机数,更严格的SQL引擎可能更有助于给你一个错误。)

我知道您希望the ID和日期对应于给定分组的最高分数,但这在您的代码中并不明确。你需要一个subselect或一个self-join来使它明确!

答案 2 :(得分:1)

使用:

SELECT t.id, 
       t.gameid, 
       t.userid, 
       t.name,
       t.score,
       t.date        
  FROM HIGHSCORES t
  JOIN (SELECT hs.gameid, 
               hs.userid,
               MAX(hs.score) 'max_score'
          FROM HIGHSCORES hs
      GROUP BY hs.gameid, hs.userid) mhs ON mhs.gameid = t.gameid
                                        AND mhs.userid = t.userid
                                        AND mhs.max_score = t.score
 WHERE t.userid = '2345' 
相关问题