在MySQL中查找每个组的最高n值

时间:2011-06-30 22:50:58

标签: mysql greatest-n-per-group

我有一些格式如下:

Lane         Series
1            680
1            685
1            688
2            666
2            425
2            775
...

而且我想要获得每个泳道最高的n系列(假设这个例子让我们说2个,但它可能会比这更多)

所以输出应该是:

Lane         Series
1            688
1            685
2            775
2            666

获得每个泳道的最高系列很容易,但我似乎无法找到获得最高2个结果的方法。

我使用带有GROUP BY的MAX聚合函数来获取MAX,但是没有像SQL Server中那样的“TOP N”函数,并且使用ORDER BY ... LIMIT仅返回总体上最高的N个结果,而不是每个通道。

由于我使用JAVA应用程序,我编码自己查询数据库并选择N是什么,我可以做一个循环并使用LIMIT并遍历每个通道,每次都进行不同的查询,但我想学习如何用MySQL做到这一点。

4 个答案:

答案 0 :(得分:5)

请参阅我的其他答案,仅针对MySQL,但非常快,解决方案。

此解决方案允许您为每个通道指定任意数量的顶行,并且不使用任何MySQL“时髦”语法 - 它应该在大多数数据库上运行。

select lane, series
from lane_series ls
group by lane, series
having (
    select count(*) 
    from lane_series
    where lane = ls.lane
    and series > ls.series) < 2 -- Here's where you specify the number of top rows
order by lane, series desc;

测试输出:

create table lane_series (lane int, series int);

insert into lane_series values 
(1, 680),
(1, 685),
(1, 688),
(2, 666),
(2, 425),
(2, 775);

select lane, series
from lane_series ls
group by lane, series
having (select count(*) from lane_series where lane = ls.lane and series > ls.series) < 2
order by lane, series desc;

+------+--------+
| lane | series |
+------+--------+
|    1 |    688 |
|    1 |    685 |
|    2 |    775 |
|    2 |    666 |
+------+--------+
4 rows in set (0.00 sec)

答案 1 :(得分:3)

这个解决方案是MySQL最快的解决方案,适用于非常大的表,但它使用“时髦”的MySQL功能,所以不会用于其他数据库版本。

(编辑在应用逻辑之前对进行排序)

set @count:=-1, @lane:=0; 
select lane, series
from (select lane, series from lane_series order by lane, series desc) x
where if(lane != @lane, @count:=-1, 0) is not null
and if(lane != @lane, @lane:=lane, lane) is not null
and (@count:=@count+1) < 2; -- Specify the number of row at top of each group here

要将此查询放在类固醇上,请在lane和series上定义一个索引:CREATE INDEX lane_series_idx on lane_series(lane, series);它将执行(超快速)仅索引扫描 - 因此您的其他文本列不会影响它。

此查询的优点是:

  1. 它只需要一个表传递(尽管已排序)
  2. 它可以处理任何级别的关系,例如,如果第二个是平局,则只会显示第二个一个 - 即行数是绝对的且永远不会超过
  3. 这是测试输出:

    create table lane_series (lane int, series int);
    
    insert into lane_series values (1, 680),(1, 685),(1, 688),(2, 666),(2, 425),(2, 775);
    
    -- Execute above query:
    
    +------+--------+
    | lane | series |
    +------+--------+
    |    1 |    688 |
    |    1 |    685 |
    |    2 |    775 |
    |    2 |    666 |
    +------+--------+
    

答案 2 :(得分:2)

这将有效,如果你知道你永远不会有第一名的关系:

SELECT lane,MAX(series)
FROM scores
GROUP BY lane
UNION 
SELECT s.lane,MAX(s.series)
FROM scores AS s
JOIN (
    SELECT lane,MAX(series) AS series
    FROM scores
    GROUP BY lane
) AS x ON (x.lane = s.lane)
WHERE s.series <> x.series
GROUP BY s.lane;

答案 3 :(得分:0)

我认为@ Bohemian的通用答案也可以写成连接而不是子查询,尽管它可能没什么区别:

select ls1.lane, ls1.series
from lane_series ls1 left join lane_series ls2 on lane
where ls1.series < ls2.series
group by ls1.lane, ls1.series
having count(ls2.series) < 2 -- Here's where you specify the number of top rows
order by ls1.lane, ls1.series desc;