获取每组分组sql结果的最大值记录

omtl5h9j  于 2021-06-21  发布在  Mysql
关注(0)|答案(17)|浏览(535)

如何获得包含每个分组集的最大值的行?
我在这个问题上看到过一些过于复杂的变化,没有一个能给出好的答案。我试着把最简单的例子放在一起:
如果有这样一个表,其中有person、group和age列,那么您如何获得每组中最年长的人(一组中的平局应给出第一个按字母顺序排列的结果)

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
zqdjd7g9

zqdjd7g91#

您可以针对一个子查询进行联接,该子查询将 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;
50few1ms

50few1ms2#

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

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

这是因为在mysql中不允许聚合非groupby列,在这种情况下mysql只返回第一行。解决方案是首先对数据进行排序,这样对于每个组,您需要的行是第一个,然后按您需要的值所在的列进行分组。
您可以避免使用复杂的子查询来查找 max() 等等,以及当有多行具有相同的最大值时返回多行的问题(其他答案也会这样做)
注意:这是一个仅限mysql的解决方案。我所知道的所有其他数据库都会抛出sql语法错误,并显示消息“groupby子句中未列出非聚合列”或类似消息。因为此解决方案使用未记录的行为,所以更谨慎的人可能希望包含一个测试,以Assert如果mysql的未来版本更改此行为,它仍然有效。

版本5.7更新:

从5.7版开始 sql-mode 设置包括 ONLY_FULL_GROUP_BY 默认情况下,要使此功能正常工作,必须没有此选项(编辑服务器的选项文件以删除此设置)。

ax6ht2ek

ax6ht2ek3#

采用排名法。

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

这个sql可以用下面的方式来表示,
select*from users,(select@rn:=0)r order by groupa、age desc、person
@上一个grp为空
@rn:=case when@prev\u grp<>groupa then 1 else@rn+1 end
这是一个三运算符表达式
这样,如果prev\u grp!=其他组rn=rn+1
让rn=1过滤掉您需要的行

tgabmvqs

tgabmvqs4#

我不会使用组作为列名,因为它是保留字。但是,遵循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
mgdq6dx1

mgdq6dx15#

axiac的解决方案最终对我最有效。然而,我有一个额外的复杂性:一个计算出的“最大值”,来自两列。
让我们用同一个例子:我想要每组中年龄最大的人。如果有同样年纪的人,就选个子最高的人。
我必须执行两次左连接才能得到这种行为:

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;

希望这有帮助!我想应该有更好的方法。。。

8i9zcol2

8i9zcol26#

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

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

它使用groupconcat来创建一个有序的concat列表,然后我只将子字符串设置为第一个。

oprakyz7

oprakyz77#

在oracle中,下面的查询可以给出所需的结果。

SELECT group,person,Age,
  ROWNUMBER() OVER (PARTITION BY group ORDER BY age desc ,person asc) as rankForEachGroup
  FROM tablename where rankForEachGroup=1
b09cbbtk

b09cbbtk8#

这就是我在mysql中获得每组n个最大行数的方法

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 每组n个元素由 ) < 1 所以对于3个元素-<3
获取最大值或最小值取决于: co.id < ci.id co.id<ci.id-最大值
co.id>ci.id-最小值
完整示例如下:
mysql选择每个组的n个最大值

r1zk6ea1

r1zk6ea19#

这种方法的好处是允许您按不同的列进行排序,而不会破坏其他数据。在您试图用一列列出订单的情况下,它非常有用,首先列出最重的订单。
资料来源:http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-康卡特

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

uwopmtnx10#

使用ctes-通用表表达式:

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
lf5gs5x2

lf5gs5x211#

如果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
    )
jc3wubiy

jc3wubiy12#

让表名为people

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;
fiei3ece

fiei3ece13#

正确的解决方案是:

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 列中没有其组的最大值 Age 将匹配中的一行或多行 b .
这个 LEFT JOIN 使其与组中最年长的人(包括组中单独的人)匹配一行 NULL 来自 b (“组中没有最大年龄”)。
使用 INNER JOIN 使这些行不匹配并忽略它们。
这个 WHERE 子句只保留具有 NULL 在从 b . 他们是每组中年龄最大的人。

进一步阅读

在《sql反模式:避免数据库编程的陷阱》一书中解释了此解决方案和其他许多解决方案

gcxthw6b

gcxthw6b14#

不确定mysql是否有行数函数。如果是这样,你可以用它来得到想要的结果。在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;
mwecs4sa

mwecs4sa15#

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`

相关问题