当前位置: 首页 > 面试题库 >

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

白嘉石
2023-03-14
问题内容

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

Id   Name   Other_Columns
-------------------------
1    A       A_data_1
2    A       A_data_2
3    A       A_data_3
4    B       B_data_1
5    B       B_data_2
6    C       C_data_1

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

1    A       A_data_1
4    B       B_data_1
6    C       C_data_1

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

3    A       A_data_3
5    B       B_data_2
6    C       C_data_1

即,应返回每个组中的最后一条记录。

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

SELECT
  *
FROM (SELECT
  *
FROM messages
ORDER BY id DESC) AS x
GROUP BY name

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


问题答案:

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

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

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

我这样写解决方案:

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

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

例如,我有一个StackOverflow
August数据转储
的副本。我将其用于基准测试。该Posts表中有1,114,357行。它在Macbook
Pro 2.40GHz的MySQL 5.0.75上运行。

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

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

SELECT p1.postid
FROM Posts p1
INNER JOIN (SELECT pi.owneruserid, MAX(pi.postid) AS maxpostid
            FROM Posts pi GROUP BY pi.owneruserid) p2
  ON (p1.postid = p2.maxpostid)
WHERE p1.owneruserid = 20860;

1 row in set (1 min 17.89 sec)

甚至EXPLAIN分析也要花费超过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.postid
FROM Posts p1 LEFT JOIN posts p2
  ON (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;


 类似资料:
  • 问题内容: ,提供了此示例以检索一组参数的最后一条记录(以下示例为计算机名中的每个值检索最新更新): 但是,在我的情况下,“ lastupdate”不是唯一的(某些更新成批出现并且具有相同的lastupdate值,并且如果两个“ computername”更新同时出现在同一批中,则您将获得“ computername + lastupdate”的非唯一输出”)。假设我还有一个字段“ rowId”只

  • 问题内容: 这个问题已经在这里有了答案 : 8年前关闭。 可能重复: SQL Server:只有GROUP BY中的最后一个条目 我有一个这样的表: 我使用以下查询: 以上查询的结果: 在结果组上方按名称排列行,但显示每个组的第一行。我想从每个组中选择最后一行(按ID)。 例如: 如何编写以上结果的查询。 谢谢 问题答案: 甚至可能不再需要该组。

  • 问题内容: 我有此数据: 代码(对于SQL Server 2005): 我想为每个名称获取具有最新DATE的ID。像这样: 实现此目的最优雅的方法是什么? 问题答案: 尽量避免使用保留字(和模糊的列名),例如…

  • 问题内容: 如标题所示,我想选择以分组的每组行的第一行和最后一行。 我在此表中包含以下数据: 我需要获取的是列开头的第一个值和列末尾的最后一个值,并按group by group列进行分组。 结果表应如下所示: 我使用和和进行了查询。它可以在SQL Server 2012中使用,但不能在SQL Server 2008中使用。我需要一个可以在SQL Server 2008中执行的查询。 问题答案:

  • 问题内容: 假设我有这样的pandas DataFrame: 我想获得一个新的DataFrame,其中每个ID的前2个记录如下: 我可以对分组依据中的记录进行编号: 但是,有没有更有效/更优雅的方法来做到这一点?还有一种更优雅的方法来对每个组中的数字进行记录(例如SQL窗口函数row_number())。 问题答案: 你试过了吗 Ouput生成: (请记住,根据数据,你可能需要先进行订购/排序)

  • 问题内容: 有没有一种方法可以检查我是否在最后一条记录上?谢谢 问题答案: 使用此模式来标识和处理结果的最后一行: