如何获取包含每个分组集的最大值的行?
我在这个问题上看到了一些过于复杂的变化,没有一个有很好的答案。我试图把最简单的例子放在一起:
根据下面的表格,使用人员,组和年龄列,您将如何获得每个组中最老的人? (组内的平局应该给出第一个字母结果)
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
答案 0 :(得分:244)
正确的解决方案是:
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
中Group
列中具有相同值的所有行以及列Age
中的较大值。来自o
的{{1}}中没有其最大值的任何行将匹配Age
中的一行或多行。
b
使其与群组中最老的人(包括其群组中的独立人员)匹配,其中LEFT JOIN
来自NULL
{&#39}。没有最大的年龄组#);
使用b
会使这些行不匹配,但会被忽略。
INNER JOIN
子句仅保留从WHERE
中提取的字段中具有NULL
s的行。他们是每个群体中最老的人。
本书SQL Antipatterns: Avoiding the Pitfalls of Database Programming
中解释了此解决方案和许多其他解决方案答案 1 :(得分:126)
在mysql中有一种超级简单的方法:
select *
from (select * from mytable order by `Group`, age desc, Person) x
group by `Group`
这是有效的,因为在mysql中你被允许不聚合非分组列,在这种情况下,mysql只返回第一个行。解决方案是首先对数据进行排序,使得对于每个组,您想要的行是第一个,然后按您想要值的列进行分组。
您可以避免尝试查找max()
等的复杂子查询,以及当多个行具有相同最大值时返回多行的问题(与其他答案一样)
注意:这是仅限mysql的解决方案。我所知道的所有其他数据库都会抛出SQL语法错误,并显示消息“非聚合列未列在group by子句中”或类似内容。由于此解决方案使用未记录的行为,因此,如果未来版本的MySQL更改此行为,则可能需要包含测试以断言保持正常工作。
从版本5.7开始,默认情况下sql-mode
设置包含ONLY_FULL_GROUP_BY
,因此要使其工作,您必须不拥有此选项(将服务器的选项文件编辑为删除此设置)。
答案 2 :(得分:33)
您可以加入一个提取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;
答案 3 :(得分:27)
我对SQLite(可能还有MySQL)的简单解决方案:
SELECT *, MAX(age) FROM mytable GROUP BY `Group`;
然而,它在PostgreSQL和其他一些平台上都不起作用。
在PostgreSQL中,您可以使用DISTINCT ON子句:
SELECT DISTINCT ON ("group") * FROM "mytable" ORDER BY "group", "age" DESC;
答案 4 :(得分:3)
使用排名方法。
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
答案 5 :(得分:2)
axiac的解决方案最终对我最有用。然而,我有一个额外的复杂性:计算出的&#34;最大值&#34;,从两列派生。
让我们使用相同的例子:我希望每组中最年长的人。如果有同样年纪的人,请选择最高的人。
我必须执行左连接两次以获得此行为:
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;
希望这有帮助!我想应该有更好的方法来做到这一点......
答案 6 :(得分:2)
不确定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;
答案 7 :(得分:1)
改进 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
答案 8 :(得分:1)
使用WHERE IN
SELECT a.* FROM `mytable` AS a
WHERE a.age IN( SELECT MAX(b.age) AS age FROM `mytable` AS b GROUP BY b.group )
ORDER BY a.group ASC, a.person ASC
答案 9 :(得分:1)
我的解决方案只有在您只需要检索一列时才有效,但是对于我的需求而言,这是在性能方面找到的最佳解决方案(它只使用一个查询!):
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来创建一个有序的concat列表,然后我只子串到第一个。
答案 10 :(得分:1)
使用CTE - 通用表格表达式:
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
答案 11 :(得分:0)
此方法的好处是允许您按其他列排名,而不是删除其他数据。在您尝试列出包含项目列的订单的情况下,它非常有用,首先列出最重的订单。
来源: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;
答案 12 :(得分:0)
让表名为人
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;
答案 13 :(得分:0)
我不会将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
答案 14 :(得分:0)
您也可以尝试
SELECT * FROM mytable WHERE age IN (SELECT MAX(age) FROM mytable GROUP BY `Group`) ;
答案 15 :(得分:0)
如果需要来自mytable
的ID(和所有coulmns)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
)
答案 16 :(得分:0)
这就是我在mysql中获得每组N max行的方式
SELECT co.id, co.person, co.country
FROM person co
WHERE (
SELECT COUNT(*)
FROM person ci
WHERE co.country = ci.country AND co.id < ci.id
) < 1
;
它如何运作:
co.country = ci.country
) < 1
控制,因此对于3个元素 - )&lt; 3 co.id < ci.id
这里有完整的例子:
答案 17 :(得分:0)
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`
答案 18 :(得分:0)
在Oracle中,以下查询可以给出所需的结果。
SELECT group,person,Age,
ROWNUMBER() OVER (PARTITION BY group ORDER BY age desc ,person asc) as rankForEachGroup
FROM tablename where rankForEachGroup=1