检索每个组中的最后一条记录-MySQL

有一个表messages包含如下所示的数据:

Id   Name   Other_Columns-------------------------1    A       A_data_12    A       A_data_23    A       A_data_34    B       B_data_15    B       B_data_26    C       C_data_1

如果我运行查询select * from messages group by name,我将得到结果:

1    A       A_data_14    B       B_data_16    C       C_data_1

什么查询将返回以下结果?

3    A       A_data_35    B       B_data_26    C       C_data_1

也就是说,应该返回每个组中的最后一条记录。

目前,这是我使用的查询:

SELECT*FROM (SELECT*FROM messagesORDER BY id DESC) AS xGROUP BY name

但这看起来效率很低。还有其他方法可以达到同样的结果吗?

1012512 次浏览

使用子查询返回正确的分组,因为你已经完成了一半。

试试这个:

selecta.*frommessages ainner join(select name, max(id) as maxid from messages group by name) as b ona.id = b.maxid

如果不是id,则需要最大值:

selecta.*frommessages ainner join(select name, max(other_col) as other_colfrom messages group by name) as b ona.name = b.nameand a.other_col = b.other_col

通过这种方式,您可以避免子查询中的相关子查询和/或排序,这往往非常缓慢/低效。

这里有两个建议。首先,如果mysql支持ROW_NUMBER(),它很简单:

WITH Ranked AS (SELECT Id, Name, OtherColumns,ROW_NUMBER() OVER (PARTITION BY NameORDER BY Id DESC) AS rkFROM messages)SELECT Id, Name, OtherColumnsFROM messagesWHERE rk = 1;

我假设你说的“最后”是指ID顺序中的最后一个。如果不是,相应地更改ROW_NUMBER()窗口的ORDER BY子句。如果ROW_NUMBER()不可用,这是另一个解决方案:

其次,如果没有,这通常是一个很好的方法:

SELECTId, Name, OtherColumnsFROM messagesWHERE NOT EXISTS (SELECT * FROM messages as M2WHERE M2.Name = messages.NameAND M2.Id > messages.Id)

换句话说,选择没有具有相同名称的以后ID消息的消息。

MySQL 8.0现在支持窗口功能,就像几乎所有流行的SQL实现一样。使用这个标准语法,我们可以编写每个组最大n个查询:

WITH ranked_messages AS (SELECT m.*, ROW_NUMBER() OVER (PARTITION BY name ORDER BY id DESC) AS rnFROM messages AS m)SELECT * FROM ranked_messages WHERE rn = 1;

MySQL手册中说明了查找分组最大行的这种方法和其他方法。

以下是我在2009年为这个问题写的原始答案:


我这样写解决方案:

SELECT m1.*FROM messages m1 LEFT JOIN messages m2ON (m1.name = m2.name AND m1.id < m2.id)WHERE m2.id IS NULL;

关于性能,根据数据的性质,一种解决方案或另一种解决方案可能更好。因此,您应该测试这两个查询,并使用给定数据库的性能更好的查询。

例如,我有一个StackOverflow八月数据转储的副本。我将使用它进行基准测试。Posts表中有1,114,357行。这在我的Macbook Pro 2.40GHz上的mysql 5.0.75上运行。

我将编写一个查询来查找给定用户ID(我的)的最新帖子。

首先使用@Eric在子查询中使用GROUP BY显示的技术

SELECT p1.postidFROM Posts p1INNER JOIN (SELECT pi.owneruserid, MAX(pi.postid) AS maxpostidFROM Posts pi GROUP BY pi.owneruserid) p2ON (p1.postid = p2.maxpostid)WHERE p1.owneruserid = 20860;
1 row in set (1 min 17.89 sec)

即使是#0分析也需要超过16秒:

+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+| id | select_type | table      | type   | possible_keys              | key         | key_len | ref          | rows    | Extra       |+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+|  1 | PRIMARY     | <derived2> | ALL    | NULL                       | NULL        | NULL    | NULL         |   76756 |             ||  1 | PRIMARY     | p1         | eq_ref | PRIMARY,PostId,OwnerUserId | PRIMARY     | 8       | p2.maxpostid |       1 | Using where ||  2 | DERIVED     | pi         | index  | NULL                       | OwnerUserId | 8       | NULL         | 1151268 | Using index |+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+3 rows in set (16.09 sec)

现在使用我的技术LEFT JOIN生成相同的查询结果:

SELECT p1.postidFROM Posts p1 LEFT JOIN posts p2ON (p1.owneruserid = p2.owneruserid AND p1.postid < p2.postid)WHERE p2.postid IS NULL AND p1.owneruserid = 20860;
1 row in set (0.28 sec)

EXPLAIN分析表明,这两个表都能够使用它们的索引:

+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+| id | select_type | table | type | possible_keys              | key         | key_len | ref   | rows | Extra                                |+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+|  1 | SIMPLE      | p1    | ref  | OwnerUserId                | OwnerUserId | 8       | const | 1384 | Using index                          ||  1 | SIMPLE      | p2    | ref  | PRIMARY,PostId,OwnerUserId | OwnerUserId | 8       | const | 1384 | Using where; Using index; Not exists |+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+2 rows in set (0.00 sec)

这是我的Posts表的DDL:

CREATE TABLE `posts` (`PostId` bigint(20) unsigned NOT NULL auto_increment,`PostTypeId` bigint(20) unsigned NOT NULL,`AcceptedAnswerId` bigint(20) unsigned default NULL,`ParentId` bigint(20) unsigned default NULL,`CreationDate` datetime NOT NULL,`Score` int(11) NOT NULL default '0',`ViewCount` int(11) NOT NULL default '0',`Body` text NOT NULL,`OwnerUserId` bigint(20) unsigned NOT NULL,`OwnerDisplayName` varchar(40) default NULL,`LastEditorUserId` bigint(20) unsigned default NULL,`LastEditDate` datetime default NULL,`LastActivityDate` datetime default NULL,`Title` varchar(250) NOT NULL default '',`Tags` varchar(150) NOT NULL default '',`AnswerCount` int(11) NOT NULL default '0',`CommentCount` int(11) NOT NULL default '0',`FavoriteCount` int(11) NOT NULL default '0',`ClosedDate` datetime default NULL,PRIMARY KEY  (`PostId`),UNIQUE KEY `PostId` (`PostId`),KEY `PostTypeId` (`PostTypeId`),KEY `AcceptedAnswerId` (`AcceptedAnswerId`),KEY `OwnerUserId` (`OwnerUserId`),KEY `LastEditorUserId` (`LastEditorUserId`),KEY `ParentId` (`ParentId`),CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`PostTypeId`) REFERENCES `posttypes` (`PostTypeId`)) ENGINE=InnoDB;

评论者注意:如果您想要使用不同版本的MySQL、不同的数据集或不同的表设计的另一个基准测试,请随时自行完成。我已经展示了上面的技术。Stack Overflow在这里向您展示了如何进行软件开发工作,而不是为您完成所有工作。

我们有没有办法使用这种方法来删除表中的重复项?结果集基本上是唯一记录的集合,所以如果我们可以删除不在结果集中的所有记录,我们实际上就没有重复项了?我试过了,但是mySQL给出了一个1093错误。

DELETE FROM messages WHERE id NOT IN(SELECT m1.idFROM messages m1 LEFT JOIN messages m2ON (m1.name = m2.name AND m1.id < m2.id)WHERE m2.id IS NULL)

有没有办法将输出保存到临时变量,然后从NOTIN(临时变量)中删除?@Bill感谢您提供了一个非常有用的解决方案。

编辑:我想我找到了解决方案:

DROP TABLE IF EXISTS UniqueIDs;CREATE Temporary table UniqueIDs (id Int(11));
INSERT INTO UniqueIDs(SELECT T1.ID FROM Table T1 LEFT JOIN Table T2 ON(T1.Field1 = T2.Field1 AND T1.Field2 = T2.Field2 #Comparison FieldsAND T1.ID < T2.ID)WHERE T2.ID IS NULL);
DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);

试试这个:

SELECT jos_categories.title AS name,joined .catid,joined .title,joined .introtextFROM   jos_categoriesINNER JOIN (SELECT *FROM   (SELECT `title`,catid,`created`,introtextFROM   `jos_content`WHERE  `sectionid` = 6ORDER  BY `id` DESC) AS yesGROUP  BY `yes`.`catid` DESCORDER  BY `yes`.`created` DESC) AS joinedON( joined.catid = jos_categories.id )

下面的查询将根据您的问题正常工作。

SELECT M1.*FROM MESSAGES M1,(SELECT SUBSTR(Others_data,1,2),MAX(Others_data) AS Max_Others_dataFROM MESSAGESGROUP BY 1) M2WHERE M1.Others_data = M2.Max_Others_dataORDER BY Others_data;

UPD: 2017-03-31,5.7.5版本MySQL默认启用了ONLY_FULL_GROUP_BY开关(因此,禁用了非确定性的GROUP BY查询)。此外,他们更新了GROUP BY实现,即使禁用了开关,解决方案也可能无法按预期工作。需要检查一下。

上述Bill Karwin的解决方案在组中的项目计数相当小时运行良好,但当组相当大时,查询的性能会变得很差,因为该解决方案只需要IS NULL比较中的n*n/2 + n/2

我在一个包含18684446行和1182组的InnoDB表上进行了测试。该表包含功能测试的测试结果,并以(test_id, request_id)作为主键。因此,test_id是一个组,我正在为每个test_id搜索最后的request_id

比尔的解决方案已经在我的戴尔e4310上运行了几个小时,我不知道它什么时候会完成,即使它在覆盖索引上运行(因此在解释中为0)。

我有几个基于相同想法的其他解决方案:

  • 如果基础索引是BTREE索引(通常情况下),最大的(group_id, item_value)对是每个group_id中的最后一个值,如果我们按降序遍历索引,则每个group_id的第一个值;
  • 如果我们读取索引覆盖的值,则按索引的顺序读取值;
  • 每个索引隐式包含附加到该索引的主键列(即主键在覆盖索引中)。在下面的解决方案中,我直接对主键进行操作,在您的情况下,您只需要在结果中添加主键列。
  • 在许多情况下,在子查询中按所需顺序收集所需的行id并将子查询的结果连接到id上要便宜得多。由于对于子查询结果中的每一行,MySQL将需要基于主键的单个获取,子查询将放在连接中的第一位,并且行将按照子查询中id的顺序输出(如果我们省略了连接的显式ORDER BY)

MySQL使用索引的3种方式是一篇很好的文章,可以了解一些细节。

解决方案1

这个速度非常快,在我的18M+行上大约需要0.8秒:

SELECT test_id, MAX(request_id) AS request_idFROM testresultsGROUP BY test_id DESC;

如果您想将顺序更改为ASC,请将其放在子查询中,仅返回id并将其用作子查询以连接到其余列:

SELECT test_id, request_idFROM (SELECT test_id, MAX(request_id) AS request_idFROM testresultsGROUP BY test_id DESC) as idsORDER BY test_id;

这需要大约1,2秒我的数据。

解决方案2

这是另一个解决方案,我的表需要大约19秒:

SELECT test_id, request_idFROM testresults, (SELECT @group:=NULL) as initWHERE IF(IFNULL(@group, -1)=@group:=test_id, 0, 1)ORDER BY test_id DESC, request_id DESC

它也以降序返回测试。由于它进行完整的索引扫描,它要慢得多,但它在这里让您了解如何为每个组输出N个最大行。

查询的缺点是它的结果不能被查询缓存缓存。

我得出了不同的解决方案,即获取每个组中最后一篇文章的ID,然后使用第一个查询的结果作为WHERE x IN构造的参数从消息表中进行选择:

SELECT id, name, other_columnsFROM messagesWHERE id IN (SELECT MAX(id)FROM messagesGROUP BY name);

我不知道与其他一些解决方案相比,它的性能如何,但它对我的表来说效果非常好,有300多万行。(4秒执行,1200多个结果)

这应该适用于MySQL和SQL服务器。

我还没有测试过大型数据库,但我认为这可能比连接表更快:

SELECT *, Max(Id) FROM messages GROUP BY Name

子查询解决方案小提琴链接

select * from messages where id in(select max(id) from messages group by Name)

解决方案按连接条件小提琴链接

select m1.* from messages m1left outer join messages m2on ( m1.id<m2.id and m1.name=m2.name )where m2.id is null

这篇文章的原因是只给小提琴链接。同样的SQL已经在其他答案中提供。

这是另一种获取最后一条相关记录的方法,使用GROUP_CONCAT和order by和SUBSTRING_INDEX从列表中选择一条记录

SELECT`Id`,`Name`,SUBSTRING_INDEX(GROUP_CONCAT(`Other_Columns`ORDER BY `Id` DESCSEPARATOR '||'),'||',1) Other_ColumnsFROMmessagesGROUP BY `Name`

上面的查询将对同一Name组中的所有Other_Columns进行分组,使用ORDER BY id DESC将按降序使用提供的分隔符将特定组中的所有Other_Columns连接起来在我的情况下,我使用了||,在这个列表上使用SUBSTRING_INDEX将选择第一个

小提琴演示

SELECTcolumn1,column2FROMtable_nameWHERE id IN(SELECTMAX(id)FROMtable_nameGROUP BY column1)ORDER BY column1 ;

Hi@Vijay Dev如果您的表消息包含id,这是自动递增主键,那么要获取主键的最新记录,您的查询应如下所示:

SELECT m1.* FROM messages m1 INNER JOIN (SELECT max(Id) as lastmsgId FROM messages GROUP BY Name) m2 ON m1.Id=m2.lastmsgId

你也可以从这里看。

http://sqlfiddle.com/#!9/ef42b/9

第一解决方案

SELECT d1.ID,Name,City FROM Demo_User d1INNER JOIN(SELECT MAX(ID) AS ID FROM Demo_User GROUP By NAME) AS P ON (d1.ID=P.ID);

第二解决方案

SELECT * FROM (SELECT * FROM Demo_User ORDER BY ID DESC) AS T GROUP BY NAME ;

如果您想要每个Name的最后一行,那么您可以按Name为每个行组提供行号,并按Id降序排列。

查询

SELECT t1.Id,t1.Name,t1.Other_ColumnsFROM(SELECT Id,Name,Other_Columns,(CASE Name WHEN @curATHEN @curRow := @curRow + 1ELSE @curRow := 1 AND @curA := Name END) + 1 AS rnFROM messages t,(SELECT @curRow := 0, @curA := '') rORDER BY Name,Id DESC)t1WHERE t1.rn = 1ORDER BY t1.Id;

SQL小提琴

这个怎么样:

SELECT DISTINCT ON (name) *FROM messagesORDER BY name, id DESC;

我有类似的问题(在postgresql艰难)和1M记录表。此解决方案需要1.7s与LEFT JOIN生成的44s。在我的情况下,我必须过滤姓名字段的对应值与NULL值,从而在0.2秒内获得更好的性能

以下是我的解决方案:

SELECTDISTINCT NAME,MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGESFROM MESSAGE;

一个相当快的方法如下。

SELECT *FROM messages aWHERE Id = (SELECT MAX(Id) FROM messages WHERE a.Name = Name)

结果

Id  Name    Other_Columns3   A   A_data_35   B   B_data_26   C   C_data_1

很明显,有很多不同的方法可以获得相同的结果,你的问题似乎是在MySQL中获得每个组的最后结果的有效方法是什么。如果你正在处理大量的数据,并假设你使用的是InnoDB和最新版本的MySQL(如5.7.21和8.0.4-rc),那么可能没有有效的方法来做到这一点。

我们有时需要对6000万行以上的表执行此操作。

对于这些示例,我将使用只有大约150万行的数据,其中查询需要查找数据中所有组的结果。在我们的实际情况下,我们通常需要从大约2,000个组返回数据(假设这不需要检查太多数据)。

我将使用以下表格:

CREATE TABLE temperature(id INT UNSIGNED NOT NULL AUTO_INCREMENT,groupID INT UNSIGNED NOT NULL,recordedTimestamp TIMESTAMP NOT NULL,recordedValue INT NOT NULL,INDEX groupIndex(groupID, recordedTimestamp),PRIMARY KEY (id));
CREATE TEMPORARY TABLE selected_group(id INT UNSIGNED NOT NULL, PRIMARY KEY(id));

温度表包含大约150万随机记录,以及100个不同的组。selected_group由这100个组填充(在我们的例子中,所有组通常小于20%)。

由于此数据是随机的,这意味着多行可以具有相同的记录时间戳。我们想要的是按照group pID的顺序获取所有选定组的列表,每个组都有最后一个记录时间戳,如果同一个组有多个类似的匹配行,那么这些行的最后一个匹配id。

如果假设MySQL有一个Last()函数,它在一个特殊的ORDER BY子句中从最后一行返回值,那么我们可以简单地执行:

SELECTlast(t1.id) AS id,t1.groupID,last(t1.recordedTimestamp) AS recordedTimestamp,last(t1.recordedValue) AS recordedValueFROM selected_group gINNER JOIN temperature t1 ON t1.groupID = g.idORDER BY t1.recordedTimestamp, t1.idGROUP BY t1.groupID;

在这种情况下只需要检查100行,因为它不使用任何普通的GROUP BY函数。这将在0秒内执行,因此效率很高。请注意,通常在MySQL中,我们会在GROUP BY子句后面看到一个ORDER BY子句,但是这个ORDER BY子句用于确定最后一个()函数的ORDER,如果它在GROUP BY之后,那么它将对GROUPS进行排序。如果没有GROUP BY子句,那么所有返回行中的最后一个值将是相同的。

然而,MySQL没有这个,所以让我们看看它有什么不同的想法,并证明这些都不是有效的。

例1

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValueFROM selected_group gINNER JOIN temperature t1 ON t1.id = (SELECT t2.idFROM temperature t2WHERE t2.groupID = g.idORDER BY t2.recordedTimestamp DESC, t2.id DESCLIMIT 1);

这检查了3,009,254行,在5.7.21上花费了约0.859秒,在8.0.4-rc上花费了稍长的时间

例2

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValueFROM temperature t1INNER JOIN (SELECT max(t2.id) AS idFROM temperature t2INNER JOIN (SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestampFROM selected_group gINNER JOIN temperature t3 ON t3.groupID = g.idGROUP BY t3.groupID) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestampGROUP BY t2.groupID) t5 ON t5.id = t1.id;

这检查了1,505,331行,在5.7.21上花费了约1.25秒,在8.0.4-rc上花费了稍长的时间

例3

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValueFROM temperature t1WHERE t1.id IN (SELECT max(t2.id) AS idFROM temperature t2INNER JOIN (SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestampFROM selected_group gINNER JOIN temperature t3 ON t3.groupID = g.idGROUP BY t3.groupID) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestampGROUP BY t2.groupID)ORDER BY t1.groupID;

这检查了3,009,685行,在5.7.21上花费了约1.95秒,在8.0.4-rc上花费了稍长的时间

例4

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValueFROM selected_group gINNER JOIN temperature t1 ON t1.id = (SELECT max(t2.id)FROM temperature t2WHERE t2.groupID = g.id AND t2.recordedTimestamp = (SELECT max(t3.recordedTimestamp)FROM temperature t3WHERE t3.groupID = g.id));

这检查了6,137,810行,在5.7.21上花费了约2.2秒,在8.0.4-rc上花费了稍长的时间

例5

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValueFROM (SELECTt2.id,t2.groupID,t2.recordedTimestamp,t2.recordedValue,row_number() OVER (PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp DESC, t2.id DESC) AS rowNumberFROM selected_group gINNER JOIN temperature t2 ON t2.groupID = g.id) t1 WHERE t1.rowNumber = 1;

这检查了6,017,808行,在8.0.4-rc上花费了约4.2秒

例6

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValueFROM (SELECTlast_value(t2.id) OVER w AS id,t2.groupID,last_value(t2.recordedTimestamp) OVER w AS recordedTimestamp,last_value(t2.recordedValue) OVER w AS recordedValueFROM selected_group gINNER JOIN temperature t2 ON t2.groupID = g.idWINDOW w AS (PARTITION BY t2.groupIDORDER BY t2.recordedTimestamp, t2.idRANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) t1GROUP BY t1.groupID;

这检查了6,017,908行,在8.0.4-rc上花费了约17.5秒

例7

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValueFROM selected_group gINNER JOIN temperature t1 ON t1.groupID = g.idLEFT JOIN temperature t2ON t2.groupID = g.idAND (t2.recordedTimestamp > t1.recordedTimestampOR (t2.recordedTimestamp = t1.recordedTimestamp AND t2.id > t1.id))WHERE t2.id IS NULLORDER BY t1.groupID;

这个花了很长时间,所以我不得不杀了它。

如果性能确实是您关心的问题,您可以在表上引入一个名为IsLastInGroup的BIT类型的新列。

在最后的列上将其设置为true,并在每一行插入/更新/删除时维护它。写入会更慢,但您将受益于读取。这取决于您的用例,我建议仅在您以读取为中心时使用它。

因此,您的查询将如下所示:

SELECT * FROM Messages WHERE IsLastInGroup = 1
SELECT * FROM table_name WHERE primary_key IN (SELECT MAX(primary_key) FROM table_name GROUP BY column_name )

您可以通过计数分组,也可以获取分组的最后一项,例如:

SELECTuser,COUNT(user) AS count,MAX(id) as lastFROM requestGROUP BY user

我们将看看如何使用MySQL来获取记录组中的最后一条记录。例如,如果您有此结果集的帖子。

idcategory_idpost_title
11标题1
21标题2
31标题3
42标题4
52标题5
63标题6

我希望能够获得每个类别中的最后一篇文章,即标题3、标题5和标题6。要按类别获取帖子,您将使用MySQL Group By键盘。

select * from posts group by category_id

但是我们从这个查询中得到的结果是。

idcategory_idpost_title
11标题1
42标题4
63标题6

group by将始终返回结果集中组中的第一条记录。

SELECT id, category_id, post_titleFROM postsWHERE id IN (SELECT MAX(id)FROM postsGROUP BY category_id );

这将返回每个组中ID最高的帖子。

idcategory_idpost_title
31标题3
52标题5
63标题6

参考点击这里

**

你好,这个查询可能会有所帮助:

**

SELECT*FROMmessage
WHERE`Id` IN (SELECTMAX(`Id`)FROMmessageGROUP BY`Name`)ORDER BY`Id` DESC

另一种方法:

找到每个程序的最大m2_price属性(1个程序中有n个属性):

select * from properties pjoin (select max(m2_price) as max_pricefrom propertiesgroup by program_id) p2 on (p.program_id = p2.program_id)having p.m2_price = max_price

希望下面的Oracle查询可以帮助:

WITH Temp_table AS(Select id, name, othercolumns, ROW_NUMBER() over (PARTITION BY name ORDER BY IDdesc)as rank from messages)Select id, name,othercolumns from Temp_table where rank=1

我在https://dzone.com/articles/get-last-record-in-each-mysql-group中找到最佳解决方案

select * from `data` where `id` in (select max(`id`) from `data` group by `name_id`)

关于什么:

select *, max(id) from messages group by name

我在sqlite上测试过它,它返回所有列和所有名称的最大id值。

MariaDB 10.3及更新版本使用GROUP_CONCAT

这个想法是使用ORDER BY+LIMIT

SELECT GROUP_CONCAT(id ORDER BY id DESC LIMIT 1) AS id,name,GROUP_CONCAT(Other_columns ORDER BY id DESC LIMIT 1) AS Other_columnsFROM tGROUP BY name;

db<>小提琴演示

如果您需要分组查询中文本列的最新或最旧记录,并且您不想使用子查询,您可以这样做…

你有一个电影列表,需要得到该系列和最新电影的数量

id系列name
1星球大战新的希望
2星球大战帝国反击战
3星球大战绝地归来
SELECT COUNT(id), series, SUBSTRING(MAX(CONCAT(id, name)), LENGTH(id) + 1),FROM MoviesGROUP BY series

这个返回…

id系列name
3星球大战绝地归来

MAX将返回具有最高值的行,因此通过将id连接到名称,您现在将获得最新记录,然后只需去掉id即可获得最终结果。

比使用子查询更有效。

对于给定的示例:

SELECT MAX(Id), Name, SUBSTRING(MAX(CONCAT(Id, Other_Columns)), LENGTH(Id) + 1),FROM messagesGROUP BY Name

快乐编码,愿原力与你同在:)

从MySQL 8.0.14开始,这也可以使用横向导出表来实现:

SELECT t.*FROM messages tJOIN LATERAL (SELECT name, MAX(id) AS idFROM messages t1WHERE t.name = t1.nameGROUP BY name) trn ON t.name = trn.name AND t.id = trn.id

db<>fiddle

这是一个更有效的版本,在1行中,只要表有时间戳列就可以工作。

SELECT Id, Name, SUBSTRING_INDEX(MAX(CONCAT(TimeStamp, ',', Other_Columns)), ',', -1)FROM MessagesORDER BY id DESC GROUP BY Name

这将返回“Other_Columns”组的最新记录

还有一个没有子查询的选项。

此解决方案使用MySQL#0窗口函数,利用#1可用的MySQL工具。

SELECT DISTINCTLAST_VALUE(Id)OVER(PARTITION BY NameORDER     BY IdROWS BETWEEN 0 PRECEDINGAND UNBOUNDED FOLLOWING),Name,LAST_VALUE(Other_Columns)OVER(PARTITION BY NameORDER     BY IdROWS BETWEEN 0 PRECEDINGAND UNBOUNDED FOLLOWING)FROMtab

试试看这里

我也有类似的问题

子查询并加入救援

SELECT p."Date",p."Symbol",p."ratio_roll_qtr_ret"FROM PUBLIC."prices_vw" AS pJOIN (SELECT "Symbol",max("Date")FROM PUBLIC."prices_vw"GROUP BY "Symbol") AS sq ON p."Date" = sq."max"AND p."Symbol" = sq."Symbol"WHERE p."ratio_roll_qtr_ret" IS NOT NULLORDER BY "ratio_roll_qtr_ret" DESC;