获取每组分组结果的前n个logging

以下是最简单的例子,尽pipe任何解决scheme都应该能够扩展到n个最需要的结果:

给出如下的表格,包括人员,组别和年龄栏,你将如何得到每个组中的两个最老的人? (组内联系不应该产生更多的结果,但按字母顺序给出前两个)

 + -------- + ------- + ----- +
 | 人|  Group | 年龄|
 + -------- + ------- + ----- +
 |  Bob |  1 |  32 |
 | 吉尔|  1 |  34 |
 |  Shawn |  1 |  42 |
 | 杰克|  2 |  29 |
 | 保罗|  2 |  36 |
 | 劳拉|  2 |  39 |
 + -------- + ------- + ----- +

预期结果集:

 + -------- + ------- + ----- +
 |  Shawn |  1 |  42 |
 | 吉尔|  1 |  34 |
 | 劳拉|  2 |  39 |
 | 保罗|  2 |  36 |
 + -------- + ------- + ----- +

注意:这个问题是build立在前一个问题上的 – 为每个分组的SQL结果获取最大值的logging – 从每个组获得一个最高行,并从@Bohemian收到一个很好的MySQL特定答案:

select * from (select * from mytable order by `Group`, Age desc, Person) x group by `Group` 

会喜欢能够build立这个,虽然我不知道如何。

这是使用UNION ALL (请参阅SQL小提琴和演示 )的一种方法。 这适用于两个组,如果您有两个以上的组,那么您需要指定group号并为每个group添加查询:

 ( select * from mytable where `group` = 1 order by age desc LIMIT 2 ) UNION ALL ( select * from mytable where `group` = 2 order by age desc LIMIT 2 ) 

有多种方法可以做到这一点,请参阅本文以确定适合您情况的最佳路线:

http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/

编辑:

这也可以为你工作,它会为每个logging生成一个行号。 使用上面链接中的一个例子,将只返回行号小于或等于2的那些logging:

 select person, `group`, age from ( select person, `group`, age, (@num:=if(@group = `group`, @num +1, if(@group := `group`, 1, 1))) row_number from test t CROSS JOIN (select @num:=0, @group:=null) c order by `Group`, Age desc, person ) as x where x.row_number <= 2; 

看演示

在其他数据库中,您可以使用ROW_NUMBER来执行此操作。 MySQL不支持ROW_NUMBER但可以使用variables来模拟它:

 SELECT person, groupname, age FROM ( SELECT person, groupname, age, @rn := IF(@prev = groupname, @rn + 1, 1) AS rn, @prev := groupname FROM mytable JOIN (SELECT @prev := NULL, @rn := 0) AS vars ORDER BY groupname, age DESC, person ) AS T1 WHERE rn <= 2 

看看它在线工作: sqlfiddle


编辑我只注意到,bluefeet发布了一个非常相似的答案:+1给他。 然而,这个答案有两个小优点:

  1. 它是一个单一的查询。 variables在SELECT语句内被初始化。
  2. 它按照问题中的描述处理关系(按字母顺序排列)。

所以我会把它留在这里,以防它可以帮助别人。

如何使用自连接:

 CREATE TABLE mytable (person, groupname, age); INSERT INTO mytable VALUES('Bob',1,32); INSERT INTO mytable VALUES('Jill',1,34); INSERT INTO mytable VALUES('Shawn',1,42); INSERT INTO mytable VALUES('Jake',2,29); INSERT INTO mytable VALUES('Paul',2,36); INSERT INTO mytable VALUES('Laura',2,39); SELECT a.* FROM mytable AS a LEFT JOIN mytable AS a2 ON a.groupname = a2.groupname AND a.age <= a2.age GROUP BY a.person HAVING COUNT(*) <= 2 ORDER BY a.groupname, a.age DESC; 

给我:

 a.person a.groupname a.age ---------- ----------- ---------- Shawn 1 42 Jill 1 34 Laura 2 39 Paul 2 36 

我受到Bill Karwin给每个类别select前10名logging的回应的强烈启发

此外,我正在使用SQLite,但这应该在MySQL上工作。

另一件事:在上面,为了方便,我用groupname列replace了group列。

编辑

在OP关于缺less领带结果的评​​论之后,我增加了对snuffin的答案,以显示所有的关系。 这意味着如果最后一个是关系,可以返回多于2行,如下所示:

 .headers on .mode column CREATE TABLE foo (person, groupname, age); INSERT INTO foo VALUES('Paul',2,36); INSERT INTO foo VALUES('Laura',2,39); INSERT INTO foo VALUES('Joe',2,36); INSERT INTO foo VALUES('Bob',1,32); INSERT INTO foo VALUES('Jill',1,34); INSERT INTO foo VALUES('Shawn',1,42); INSERT INTO foo VALUES('Jake',2,29); INSERT INTO foo VALUES('James',2,15); INSERT INTO foo VALUES('Fred',1,12); INSERT INTO foo VALUES('Chuck',3,112); SELECT a.person, a.groupname, a.age FROM foo AS a WHERE a.age >= (SELECT MIN(b.age) FROM foo AS b WHERE (SELECT COUNT(*) FROM foo AS c WHERE c.groupname = b.groupname AND c.age >= b.age) <= 2 GROUP BY b.groupname) ORDER BY a.groupname ASC, a.age DESC; 

给我:

 person groupname age ---------- ---------- ---------- Shawn 1 42 Jill 1 34 Laura 2 39 Paul 2 36 Joe 2 36 Chuck 3 112 

尝试这个:

 SELECT a.person, a.group, a.age FROM person AS a WHERE (SELECT COUNT(*) FROM person AS b WHERE b.group = a.group AND b.age >= a.age) <= 2 ORDER BY a.group ASC, a.age DESC 

DEMO

看一下这个:

 SELECT p.Person, p.`Group`, p.Age FROM people p INNER JOIN ( SELECT MAX(Age) AS Age, `Group` FROM people GROUP BY `Group` UNION SELECT MAX(p3.Age) AS Age, p3.`Group` FROM people p3 INNER JOIN (SELECT MAX(Age) AS Age, `Group` FROM people GROUP BY `Group`) p4 ON p3.Age < p4.Age AND p3.`Group` = p4.`Group` GROUP BY `Group` ) p2 ON p.Age = p2.Age AND p.`Group` = p2.`Group` ORDER BY `Group`, Age DESC, Person; 

SQL小提琴: http ://sqlfiddle.com/#!2/cdbb6/15

如果其他答案不够快给这个代码一个尝试:

 SELECT province, n, city, population FROM ( SELECT @prev := '', @n := 0 ) init JOIN ( SELECT @n := if(province != @prev, 1, @n + 1) AS n, @prev := province, province, city, population FROM Canada ORDER BY province ASC, population DESC ) x WHERE n <= 3 ORDER BY province, n; 

输出:

 +---------------------------+------+------------------+------------+ | province | n | city | population | +---------------------------+------+------------------+------------+ | Alberta | 1 | Calgary | 968475 | | Alberta | 2 | Edmonton | 822319 | | Alberta | 3 | Red Deer | 73595 | | British Columbia | 1 | Vancouver | 1837970 | | British Columbia | 2 | Victoria | 289625 | | British Columbia | 3 | Abbotsford | 151685 | | Manitoba | 1 | ... 

在SQL Server中, row_numer()是一个function强大的函数,可以轻松得到如下结果

 select Person,[group],age from ( select * ,row_number() over(partition by [group] order by age desc) rn from mytable ) t where rn <= 2