获取每组分组结果的前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给他。 然而,这个答案有两个小优点:
- 它是一个单一的查询。 variables在SELECT语句内被初始化。
- 它按照问题中的描述处理关系(按字母顺序排列)。
所以我会把它留在这里,以防它可以帮助别人。
如何使用自连接:
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