从MySQL中的表中加入单行

时间:2010-06-25 18:22:17

标签: mysql join greatest-n-per-group ranking dense-rank

我有两张表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缺乏密集排名有某种关系。什么是最好的解决方法?

2 个答案:

答案 0 :(得分:6)

这是Stack Overflow上频繁出现的最大n组问题。

这是我通常的回答:

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分享的评论,请记住,这种类型的查询会从正确的索引中获益匪浅。

答案 1 :(得分:0)

使用GROUP BY时,大多数RDMB甚至不允许在SELECT子句中包含非聚合列。在MySQL中,您最终会得到非聚合列的随机行值。如果您在所有行的特定列中实际具有相同的值,则此选项很有用。因此,MySQL不会限制我们,这很好,但这是一件很重要的事情。

SQL Antipatterns中专门讨论了这一章。