Yar*_*rin 212 mysql sql greatest-n-per-group
如何获得包含每个分组集的最大值的行?
我在这个问题上看到了一些过于复杂的变化,没有一个有很好的答案.我试图把最简单的例子放在一起:
给出如下表格,包含人,组和年龄列,您将如何获得每组中最老的人?(组内的一个平局应该给出第一个字母结果)
Person | Group | Age
---
Bob | 1 | 32
Jill | 1 | 34
Shawn| 1 | 42
Jake | 2 | 29
Paul | 2 | 36
Laura| 2 | 39
Run Code Online (Sandbox Code Playgroud)
期望的结果集:
Shawn | 1 | 42
Laura | 2 | 39
Run Code Online (Sandbox Code Playgroud)
axi*_*iac 268
正确的解决方案是:
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
Run Code Online (Sandbox Code Playgroud)
它匹配每一行,o其中所有行在列中b具有相同的值,在列中Group具有更大的值Age.在列中o没有其组的最大值的任何行将Age匹配来自的一行或多行b.
这LEFT JOIN使得它与组中最老的人(包括他们组中独自的人)匹配,其中一行充满了NULLs b('组中没有最大年龄').
使用INNER JOIN使这些行不匹配,它们将被忽略.
该WHERE子句仅保留NULL从中提取的字段中具有s 的行b.他们是每个群体中最年长的人.
" SQL反模式:避免数据库编程陷阱 "一书中解释了此解决方案和许多其他解决方案
Boh*_*ian 131
在mysql中有一个超级简单的方法:
select *
from (select * from mytable order by `Group`, age desc, Person) x
group by `Group`
Run Code Online (Sandbox Code Playgroud)
这工作,因为在MySQL中你被允许不聚集非组逐列,在这种情况下,MySQL的只是返回的第一排.解决方案是首先对数据进行排序,使得对于每个组,您想要的行是第一个,然后按您想要值的列进行分组.
您可以避免尝试查找max()等的复杂子查询,以及当多个行具有相同的最大值时返回多行的问题(如其他答案所做的那样)
注意:这是一个仅限mysql的解决方案.我知道的所有其他数据库都会抛出SQL语法错误,并显示消息"非聚合列未列在group by子句中"或类似内容.因为此解决方案使用未记录的行为,所以如果MySQL的未来版本更改此行为,则更谨慎可能需要包含测试以声明它仍然有效.
从5.7版开始,默认情况下sql-mode包含该设置ONLY_FULL_GROUP_BY,因此要使其工作,您必须没有此选项(编辑服务器的选项文件以删除此设置).
Mic*_*ski 42
你可以加入一个拉取MAX(Group)和的子查询Age.此方法可在大多数RDBMS中移植.
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT `Group`, MAX(Age) AS max_age
FROM yourTable
GROUP BY `Group`
) t2
ON t1.`Group` = t2.`Group` AND t1.Age = t2.max_age;
Run Code Online (Sandbox Code Playgroud)
Igo*_*gin 28
我对SQLite(可能是MySQL)的简单解决方案:
SELECT *, MAX(age) FROM mytable GROUP BY `Group`;
Run Code Online (Sandbox Code Playgroud)
但它在PostgreSQL和其他一些平台上不起作用.
在PostgreSQL中,您可以使用DISTINCT ON子句:
SELECT DISTINCT ON ("group") * FROM "mytable" ORDER BY "group", "age" DESC;
Run Code Online (Sandbox Code Playgroud)
改进axiac 的解决方案,以避免每组选择多行,同时还允许使用索引
SELECT o.*
FROM `Persons` o
LEFT JOIN `Persons` b
ON o.Group = b.Group AND o.Age < b.Age
LEFT JOIN `Persons` c
ON o.Group = c.Group AND o.Age = c.Age and o.id < c.id
WHERE b.Age is NULL and c.id is null
Run Code Online (Sandbox Code Playgroud)
不确定 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;
Run Code Online (Sandbox Code Playgroud)