为每组分组的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