MySQL为表中的每个人选择前X个记录

时间:2012-04-01 23:03:56

标签: mysql greatest-n-per-group

有没有更好的方法从MySQL表中获取多个“前X”结果?当不同的 foo 的数量很小时,我可以通过联合轻松完成此任务:

(SELECT foo,score FROM tablebar WHERE (foo = 'abc') ORDER BY score DESC LIMIT 10) 
UNION 
(SELECT foo,score FROM tablebar WHERE (foo = 'def') ORDER BY score DESC LIMIT 10)

我显然可以继续为foo的每个值添加联合。但是,当foo有500多个不同的值时,这是不实际的,我需要每个值的前X个。

1 个答案:

答案 0 :(得分:10)

这种类型的查询可以在"最大-n-group-group"中重新定义。感觉,你想要每个"组"是'foo'的价值。

我建议您查看this link,这个问题非常精彩,从一种有意义的方式开始执行查询并逐步优化它。

set @num := 0, @foo := '';
select foo, score
from (
   select foo, score,
      @num := if(@foo = foo, @num + 1, 1) as row_number,
      @foo := foo as dummy
  from tablebar
  where foo IN ('abc','def')
  order by foo, score DESC     
) as x where x.row_number <= 10;

如果您希望在所有级别foo执行此操作(即想象执行GROUP BY foo),则可以省略where foo in ...行。

基本上,内部查询(SELECT foo, score FROM tablebar WHERE foo IN ('abc','def') ORDER BY foo, score DESC)从表格中抓取fooscore,先按foo排序,然后按降序排列。

@num := ...只会增加每一行,每foo的新值重置为1。也就是说,@num只是行号/排名(尝试单独运行内部查询以查看我的意思)。

外部查询然后选择排名/行号小于或等于10的行。

注:

使用UNION的原始查询会删除重复项,因此如果foo='abc'的前10个分数都是100,那么只返回一行(因为(foo,score)对被复制10次)。这个将返回重复。