4

我有两张桌子playersscores.

我想生成一个看起来像这样的报告:

player    first score             points
foo       2010-05-20              19
bar       2010-04-15              29
baz       2010-02-04              13

现在,我的查询看起来像这样:

select p.name        player,
       min(s.date)   first_score,
       s.points      points    
from  players p    
join  scores  s on  s.player_id = p.id    
group by p.name, s.points

我需要与返回s.points的行相关联的那个min(s.date)。这个查询会发生这种情况吗?也就是说,我如何确定我得到s.points了连接行的正确值?

旁注:我想这在某种程度上与 MySQL 缺乏密集排名有关。这里最好的解决方法是什么?

4

2 回答 2

6

这是 Stack Overflow 上经常出现的最大每组问题。

这是我通常的回答:

select
  p.name        player,
  s.date        first_score,
  s.points      points

from  players p

join  scores  s
  on  s.player_id = p.id

left outer join scores  s2
  on  s2.player_id = p.id
      and s2.date < s.date

where
  s2.player_id is null

;

换句话说,给定分数 s,尝试为同一玩家找到分数 s2,但日期更早。如果没有找到更早的分数,则 s 是最早的分数。


关于您对领带的评论:您必须制定一项政策,以防出现领带时使用哪个政策。一种可能性是,如果您使用自动递增的主键,则具有最小值的主键是较早的主键。请参阅下面的外部连接中的附加术语:

select
  p.name        player,
  s.date        first_score,
  s.points      points

from  players p

join  scores  s
  on  s.player_id = p.id

left outer join scores  s2
  on  s2.player_id = p.id
      and (s2.date < s.date or s2.date = s.date and s2.id < s.id)

where
  s2.player_id is null

;

基本上,您需要添加决胜局条款,直到您到达一个保证唯一的列,至少对于给定的玩家而言。表的主键通常是最好的解决方案,但我见过其他列适合的情况。

关于我与@OMG Ponies 分享的评论,请记住,这种类型的查询极大地受益于正确的索引。

于 2010-06-25T18:26:58.183 回答
0

在使用 GROUP BY 时,大多数 RDMB 甚至不允许您在 SELECT 子句中包含非聚合列。在 MySQL 中,您最终会得到来自非聚合列的随机行的值。如果您在所有行的特定列中实际上具有相同的值,这将很有用。因此,很高兴 MySQL 没有限制我们,尽管理解这一点很重要。

SQL Antipatterns中有一整章专门讨论这个问题。

于 2010-06-25T18:30:56.943 回答