为每组分组的SQL结果获取最大值的logging

你如何获得包含每个分组集合的最大值的行?

我在这个问题上看到了一些过于复杂的变化,没有一个答案很好。 我试图把最简单的例子放在一起:

给出如下的表格,包括人员,组别和年龄栏,你将如何得到每组中最年长的人? (一个组内的领带应该给第一个字母的结果)

Person | Group | Age --- Bob | 1 | 32 Jill | 1 | 34 Shawn| 1 | 42 Jake | 2 | 29 Paul | 2 | 36 Laura| 2 | 39 

预期结果集:

 Shawn | 1 | 42 Laura | 2 | 39 

在mysql中有一个非常简单的方法:

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

这是可行的,因为在mysql中,您不允许聚合非分组列,在这种情况下,mysql只是返回第一行。 解决的办法是先对数据进行sorting,以便每个组首先select所需的行,然后按您想要的值进行分组。

你避免了复杂的子查询,试图findmax()等,也有返回多个行时,有多个具有相同的最大值(如其他答案会做)

注意:这是一个只有mysql的解决scheme。 我所知道的所有其他数据库都会抛出一个SQL语法错误消息“非聚合列不在group by子句中列出”或类似的消息。 一些“纯粹主义者”认为这个语法是恶魔的工作,但是这真是太方便了!

版本5.7更新:

自5.7版以来, sql-mode设置默认包括ONLY_FULL_GROUP_BY ,所以为了使这个工作成功,你不能有这个选项(编辑服务器的选项文件来删除这个设置)。

正确的解决scheme是:

 SELECT o.* FROM `Persons` o # 'o' from 'oldest person in group' LEFT JOIN `Persons` b # 'b' from 'bigger age' ON o.Group = b.Group AND o.Age < b.Age WHERE b.Age is NULL # bigger age not found 

怎么运行的:

它将来自o每一行与b列中具有相同值的所有行相匹配,并在列Age匹配更大的值。 o列中没有其组的最大值的任何行将匹配来自b一个或多个行。

LEFT JOIN使得它与从b ('没有最大年龄的组')中满足NULL的行匹配组中最老的人(包括在他们组中单独的人)。
使用INNER JOIN使这些行不匹配,它们被忽略。

WHERE子句只保留从b提取的字段中具有NULL的行。 他们是每个组别中年龄最大的人。

更多的读数

SQL解决scheme:避免数据库编程的陷阱 “一书中解释了这个解决scheme和许多其他解决scheme

你可以join一个抽取MAX(Group)Age的子查询。 这种方法在大多数RDBMS中是可移植的。

 SELECT yourtable.* FROM yourtable JOIN ( SELECT `Group`, MAX(Age) AS age FROM yourtable GROUP BY `Group` ) maxage /* join subquery against both Group and Age values */ ON yourtable.`Group` = maxage.`Group` AND yourtable.Age = maxage.age 

我简单的SQLite解决scheme(可能是MySQL):

 SELECT *, MAX(age) FROM mytable GROUP BY `Group`; 

但是它在PostgreSQL和其他一些平台上不起作用。

在PostgreSQL中,你可以使用DISTINCT ON子句:

 SELECT DISTINCT ON ("group") * FROM "mytable" ORDER BY "group", "age" DESC; 

使用排名方法。

 SELECT @rn := CASE WHEN @prev_grp <> groupa THEN 1 ELSE @rn+1 END AS rn, @prev_grp :=groupa, person,age,groupa FROM users,(SELECT @rn := 0) r HAVING rn=1 ORDER BY groupa,age DESC,person 

使用CTE – 公用表expression式:

 WITH MyCTE(MaxPKID, SomeColumn1) AS( SELECT MAX(a.MyTablePKID) AS MaxPKID, a.SomeColumn1 FROM MyTable1 a GROUP BY a.SomeColumn1 ) SELECT b.MyTablePKID, b.SomeColumn1, b.SomeColumn2 MAX(b.NumEstado) FROM MyTable1 b INNER JOIN MyCTE c ON c.MaxPKID = b.MyTablePKID GROUP BY b.MyTablePKID, b.SomeColumn1, b.SomeColumn2 --Note: MyTablePKID is the PrimaryKey of MyTable 

axiac的解决scheme是最适合我的。 然而,我有一个额外的复杂性:计算“最大值”,从两列中得出。

让我们用同样的例子:我想每个组中最老的人。 如果有同样老的人,就拿最高的人。

我不得不执行左连接两次以获得此行为:

 SELECT o1.* WHERE (SELECT o.* FROM `Persons` o LEFT JOIN `Persons` b ON o.Group = b.Group AND o.Age < b.Age WHERE b.Age is NULL) o1 LEFT JOIN (SELECT o.* FROM `Persons` o LEFT JOIN `Persons` b ON o.Group = b.Group AND o.Age < b.Age WHERE b.Age is NULL) o2 ON o1.Group = o2.Group AND o1.Height < o2.Height WHERE o2.Height is NULL; 

希望这可以帮助! 我想应该有更好的办法做到这一点,虽然…

你也可以试试

 SELECT * FROM mytable WHERE age IN (SELECT MAX(age) FROM mytable GROUP BY `Group`) ; 

这种方法的好处是可以让您按照不同的列进行sorting,而不会丢弃其他数据。 在试图列出项目列的订单时,这非常有用,首先列出最重的项目。

来源: http : //dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat

 SELECT person, group, GROUP_CONCAT( DISTINCT age ORDER BY age DESC SEPARATOR ', follow up: ' ) FROM sql_table GROUP BY group; 

不确定MySQL是否有row_number函数。 如果是这样,你可以使用它来获得所需的结果。 在SQL Server上,你可以做类似于:

 CREATE TABLE p ( person NVARCHAR(10), gp INT, age INT ); GO INSERT INTO p VALUES ('Bob', 1, 32); INSERT INTO p VALUES ('Jill', 1, 34); INSERT INTO p VALUES ('Shawn', 1, 42); INSERT INTO p VALUES ('Jake', 2, 29); INSERT INTO p VALUES ('Paul', 2, 36); INSERT INTO p VALUES ('Laura', 2, 39); GO SELECT t.person, t.gp, t.age FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY gp ORDER BY age DESC) row FROM p ) t WHERE t.row = 1; 

让表名为人

 select O.* -- > O for oldest table from people O , people T where O.grp = T.grp and O.Age = (select max(T.age) from people T where O.grp = T.grp group by T.grp) group by O.grp; 

我的解决scheme只适用于只需要检索一列的情况,但是对于我的需求来说,在性能方面却是最好的解决scheme(它只使用一个查询!):

 SELECT SUBSTRING_INDEX(GROUP_CONCAT(column_x ORDER BY column_y),',',1) AS xyz, column_z FROM table_name GROUP BY column_z; 

它使用GROUP_CONCAT为了创build一个有序的concat列表,然后我substring只有第一个。

如果ID(和所有coulmns)需要从mytable

 SELECT * FROM mytable WHERE id NOT IN ( SELECT A.id FROM mytable AS A JOIN mytable AS B ON A. GROUP = B. GROUP AND A.age < B.age ) 
 with CTE as (select Person, [Group], Age, RN= Row_Number() over(partition by [Group] order by Age desc) from yourtable)` `select Person, Age from CTE where RN = 1` 

因为它是保留字,所以我不会使用Group作为列名。 但是,下面的SQL将工作。

 SELECT a.Person, a.Group, a.Age FROM [TABLE_NAME] a INNER JOIN ( SELECT `Group`, MAX(Age) AS oldest FROM [TABLE_NAME] GROUP BY `Group` ) b ON a.Group = b.Group AND a.Age = b.oldest