MySQL在Group By查询中选择错误的列值

时间:2009-04-25 02:31:23

标签: mysql database select group-by

这是我遇到的一个真正的noobish MySQL查询问题。

我在写作的游戏中有一个高分表。高分DB记录了所获得的名称,级别和分数。 db中有许多接近重复的内容。例如:

Name | Level | Score | Timestamp (key)
Bob    2       41    | 1234567.890
Bob    3       15    | 1234568.890
Bob    3       20    | 1234569.890
Joe    2       40    | 1234561.890
Bob    3       21    | 1234562.890
Bob    3       21    | 1234563.890

我想返回“达到最高级别”的高分列表,其输出类似于:

Name | Level | Score
Bob    3       21
Joe    2       40

我目前使用的SQL查询是:

SELECT *, MAX(level) as level 
FROM highscores 
GROUP BY name
ORDER BY level DESC, score DESC
LIMIT 5

然而,这并不是很有效。 “得分”字段输出似乎总是从组中随机拉出,而不是取得达到的最高级别的相应分数。例如:

Name | Level | Score
Bob    3       41
Joe    2       40
鲍勃从未在3级获得41分!我该如何解决这个问题?

3 个答案:

答案 0 :(得分:2)

您需要使用子查询来拉出分数。

select distinct
    name, 
    max(level) as level,
    (select max(score) from highscores h2 
        where h2.name = h1.name and h2.level = h1.level) as score
from highscores h1 
group by name 
order by level desc, score desc

干杯,

埃里克

让我感到烦恼的是,当我发布答案时,我没有花时间解释为什么会这样,所以这里有:

当您撤回所有内容(*),然后是最高级别时,您将获得的是每个记录的顺序,以及一个上面有最大级别的列。请注意,您不是按分数分组(这将给你Bob 2 41和Bob 3 21 - 我们的朋友Bob的两条记录)。

那么,我们如何解决这个问题呢?你需要做一个子查询来额外过滤你的结果,这就是那个(选择max(score)...)。现在,对于读取Bob的每一行,您将获得他的最高等级(3),以及他在该等级的最高分数(21)。但是,这仍然给了我们许多鲍勃的行(例如 - 如果他有5行,你将得到5行鲍勃3 21)。要将此限制为仅最高分,我们需要在select语句中使用DISTINCT子句仅返回唯一行。

更新:正确的SQL(无法评论le dorfier的帖子):

SELECT h1.Name, h1.Level, MAX(h1.Score)
    FROM highscores h1
    LEFT OUTER JOIN highscores h2 ON h1.name = h2.name AND h1.level < h2.level
    LEFT OUTER JOIN highscores h3 ON h1.name = h3.name AND h2.level = h3.level AND h1.score < h3.score
    WHERE h2.Name IS NULL AND h3.Name IS NULL
    GROUP BY h1.Name, h1.Level

答案 1 :(得分:1)

这很有效。

SELECT h1.Name,h1.Level,h1.Score
来自高分榜h1
LEFT JOIN highscores h2 ON h1.name = h2.name AND h1.level&lt; h2.level
LEFT JOIN highscores h3 ON h1.name = h3.name AND h1.level = h3.level AND h1.score&lt; h3.score
WHERE h2.id IS NULL和h3.id IS NULL

您正在寻找该用户没有更高级别的级别/分数,并且没有该级别的更高分数。

答案 2 :(得分:0)

有趣的问题。这是另一个解决方案:

SELECT hs.name, hs.level, MAX(score) AS score
FROM highscores hs
INNER JOIN (
  SELECT name, MAX(level) AS level FROM highscores GROUP BY name
) hl ON hl.name = hs.name AND hl.level = hs.level
GROUP BY hs.name, hs.level;

就个人而言,我发现这是最容易理解的,我的预感是数据库执行起来相对有效。

我最喜欢上面的查询,但只是为了踢...我发现下面的一个有趣的方式。假设得分不能超过99999 ......

SELECT name, level, score
FROM highscores hs
INNER JOIN (
  SELECT name, MAX(level * 100000 + score) AS hfactor
  FROM highscores GROUP BY name
) hf ON hf.hfactor = hs.level * 100000 + hs.score AND hf.name = hs.name;