1194

有一个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

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

4

32 回答 32

1216

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 data dump的副本。我将使用它进行基准测试。表中有 1,114,357 行Posts。这是在我的 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;

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

于 2009-08-21T17:39:46.603 回答
171

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

Bill Karwin 的上述解决方案在组内的项目数相当少时工作得很好,但是当组相当大时查询的性能变得很差,因为该解决方案n*n/2 + n/2只需要IS NULL比较。

我对带有组的18684446行的 InnoDB 表进行了测试。1182该表包含功能测试的测试结果,并具有(test_id, request_id)作为主键。因此,test_id是一个小组,我正在寻找request_id每个小组的最后一个test_id

Bill 的解决方案已经在我的 dell e4310 上运行了几个小时,我不知道它什么时候会完成,即使它在覆盖索引上运行(因此using index在 EXPLAIN 中)。

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

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

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

解决方案 1

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

SELECT test_id, MAX(request_id) AS request_id
FROM testresults
GROUP BY test_id DESC;

如果要将顺序更改为 ASC,请将其放入子查询中,仅返回 id 并将其用作子查询以加入其余列:

SELECT test_id, request_id
FROM (
    SELECT test_id, MAX(request_id) AS request_id
    FROM testresults
    GROUP BY test_id DESC) as ids
ORDER BY test_id;

这对我的数据大约需要 1.2 秒。

解决方案 2

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

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

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

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

于 2012-01-06T11:21:09.380 回答
118

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

试试这个:

select
    a.*
from
    messages a
    inner join 
        (select name, max(id) as maxid from messages group by name) as b on
        a.id = b.maxid

如果不是id你想要的最大值:

select
    a.*
from
    messages a
    inner join 
        (select name, max(other_col) as other_col 
         from messages group by name) as b on
        a.name = b.name
        and a.other_col = b.other_col

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

于 2009-08-21T17:06:42.350 回答
81

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

SELECT id, name, other_columns
FROM messages
WHERE id IN (
    SELECT MAX(id)
    FROM messages
    GROUP BY name
);

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

这应该适用于 MySQL 和 SQL Server。

于 2012-02-20T21:46:38.020 回答
49

通过子查询小提琴链接解决

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

解决方案通过加入条件小提琴链接

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

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

于 2013-12-25T08:36:42.567 回答
13

一种速度相当快的方法如下。

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

结果

Id  Name    Other_Columns
3   A   A_data_3
5   B   B_data_2
6   C   C_data_1
于 2018-03-10T20:33:11.100 回答
10

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

WITH Ranked AS (
  SELECT Id, Name, OtherColumns,
    ROW_NUMBER() OVER (
      PARTITION BY Name
      ORDER BY Id DESC
    ) AS rk
  FROM messages
)
  SELECT Id, Name, OtherColumns
  FROM messages
  WHERE rk = 1;

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

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

SELECT
  Id, Name, OtherColumns
FROM messages
WHERE NOT EXISTS (
  SELECT * FROM messages as M2
  WHERE M2.Name = messages.Name
  AND M2.Id > messages.Id
)

换言之,选择没有具有相同名称的 later-Id 消息的消息。

于 2009-08-21T17:26:12.357 回答
10

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

id category_id post_title

1 1 Title 1

2 1 Title 2

3 1 Title 3

4 2 Title 4

5 2 Title 5

6 3 Title 6

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

select * from posts group by category_id

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

id category_id post_title

1 1 Title 1

4 2 Title 4

6 3 Title 6

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

SELECT id, category_id, post_title FROM posts WHERE id IN ( SELECT MAX(id) FROM posts GROUP BY category_id );

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

id category_id post_title

3 1 Title 3

5 2 Title 5

6 3 Title 6

参考点这里

于 2019-04-27T08:03:10.887 回答
7

显然有很多不同的方法可以获得相同的结果,你的问题似乎是在 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%)。

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

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

SELECT 
  last(t1.id) AS id, 
  t1.groupID, 
  last(t1.recordedTimestamp) AS recordedTimestamp, 
  last(t1.recordedValue) AS recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
ORDER BY t1.recordedTimestamp, t1.id
GROUP BY t1.groupID;

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

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

示例 1

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
  SELECT t2.id
  FROM temperature t2 
  WHERE t2.groupID = g.id
  ORDER BY t2.recordedTimestamp DESC, t2.id DESC
  LIMIT 1
);

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

示例 2

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM temperature t1
INNER JOIN ( 
  SELECT max(t2.id) AS id   
  FROM temperature t2
  INNER JOIN (
    SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
    FROM selected_group g
    INNER JOIN temperature t3 ON t3.groupID = g.id
    GROUP BY t3.groupID
  ) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
  GROUP 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.recordedValue 
FROM temperature t1
WHERE t1.id IN ( 
  SELECT max(t2.id) AS id   
  FROM temperature t2
  INNER JOIN (
    SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
    FROM selected_group g
    INNER JOIN temperature t3 ON t3.groupID = g.id
    GROUP BY t3.groupID
  ) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
  GROUP 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.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
  SELECT max(t2.id)
  FROM temperature t2 
  WHERE t2.groupID = g.id AND t2.recordedTimestamp = (
      SELECT max(t3.recordedTimestamp)
      FROM temperature t3 
      WHERE 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.recordedValue
FROM (
  SELECT 
    t2.id, 
    t2.groupID, 
    t2.recordedTimestamp, 
    t2.recordedValue, 
    row_number() OVER (
      PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp DESC, t2.id DESC
    ) AS rowNumber
  FROM selected_group g 
  INNER 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.recordedValue 
FROM (
  SELECT 
    last_value(t2.id) OVER w AS id, 
    t2.groupID, 
    last_value(t2.recordedTimestamp) OVER w AS recordedTimestamp, 
    last_value(t2.recordedValue) OVER w AS recordedValue
  FROM selected_group g
  INNER JOIN temperature t2 ON t2.groupID = g.id
  WINDOW w AS (
    PARTITION BY t2.groupID 
    ORDER BY t2.recordedTimestamp, t2.id 
    RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  )
) t1
GROUP BY t1.groupID;

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

例 7

SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue 
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
LEFT JOIN temperature t2 
  ON t2.groupID = g.id 
  AND (
    t2.recordedTimestamp > t1.recordedTimestamp 
    OR (t2.recordedTimestamp = t1.recordedTimestamp AND t2.id > t1.id)
  )
WHERE t2.id IS NULL
ORDER BY t1.groupID;

这个是永远的,所以我不得不杀了它。

于 2018-04-18T07:45:23.327 回答
6

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

SELECT 
  `Id`,
  `Name`,
  SUBSTRING_INDEX(
    GROUP_CONCAT(
      `Other_Columns` 
      ORDER BY `Id` DESC 
      SEPARATOR '||'
    ),
    '||',
    1
  ) Other_Columns 
FROM
  messages 
GROUP BY `Name` 

上面的查询将对Other_Columns同一Name组中的所有内容进行分组,并且使用ORDER BY id DESCOther_Columns在我使用的情况下使用提供的分隔符按降序加入特定组中的所有内容||,使用SUBSTRING_INDEX此列表将选择第一个

小提琴演示

于 2014-03-29T14:51:01.050 回答
5

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

SELECT *, Max(Id) FROM messages GROUP BY Name
于 2012-03-31T14:44:05.747 回答
5
SELECT 
  column1,
  column2 
FROM
  table_name 
WHERE id IN 
  (SELECT 
    MAX(id) 
  FROM
    table_name 
  GROUP BY column1) 
ORDER BY column1 ;
于 2014-04-11T06:55:00.467 回答
5

嗨@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
于 2014-10-21T14:08:16.030 回答
4

您也可以从这里观看。

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

第一个解决方案

SELECT d1.ID,Name,City FROM Demo_User d1
INNER 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 ;
于 2015-09-28T09:07:12.013 回答
3

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

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

有没有办法将输出保存到临时变量,然后从 NOT IN(临时变量)中删除?@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 Fields  
    AND T1.ID < T2.ID) 
    WHERE T2.ID IS NULL); 

DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);
于 2010-10-08T01:10:11.490 回答
3

试试这个:

SELECT jos_categories.title AS name,
       joined .catid,
       joined .title,
       joined .introtext
FROM   jos_categories
       INNER JOIN (SELECT *
                   FROM   (SELECT `title`,
                                  catid,
                                  `created`,
                                  introtext
                           FROM   `jos_content`
                           WHERE  `sectionid` = 6
                           ORDER  BY `id` DESC) AS yes
                   GROUP  BY `yes`.`catid` DESC
                   ORDER  BY `yes`.`created` DESC) AS joined
         ON( joined.catid = jos_categories.id )  
于 2011-07-15T02:05:08.860 回答
3

这是我的解决方案:

SELECT 
  DISTINCT NAME,
  MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGES 
FROM MESSAGE;
于 2017-06-08T18:49:57.913 回答
3
SELECT * FROM table_name WHERE primary_key IN (SELECT MAX(primary_key) FROM table_name GROUP BY column_name )
于 2019-02-07T23:31:50.880 回答
3

**

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

**

SELECT 
  *
FROM 
  message 

WHERE 
  `Id` IN (
    SELECT 
      MAX(`Id`) 
    FROM 
      message 
    GROUP BY 
      `Name`
  ) 
ORDER BY 
   `Id` DESC
于 2019-10-17T10:18:49.490 回答
3

我在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`)
于 2020-10-01T12:25:59.703 回答
2

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

SELECT M1.* 
FROM MESSAGES M1,
(
 SELECT SUBSTR(Others_data,1,2),MAX(Others_data) AS Max_Others_data
 FROM MESSAGES
 GROUP BY 1
) M2
WHERE M1.Others_data = M2.Max_Others_data
ORDER BY Others_data;
于 2011-11-18T20:19:44.880 回答
2

如果您想要 each 的最后一行Name,则可以按降序为每个行组提供行NameId

询问

SELECT t1.Id, 
       t1.Name, 
       t1.Other_Columns
FROM 
(
     SELECT Id, 
            Name, 
            Other_Columns,
    (
        CASE Name WHEN @curA 
        THEN @curRow := @curRow + 1 
        ELSE @curRow := 1 AND @curA := Name END 
    ) + 1 AS rn 
    FROM messages t, 
    (SELECT @curRow := 0, @curA := '') r 
    ORDER BY Name,Id DESC 
)t1
WHERE t1.rn = 1
ORDER BY t1.Id;

SQL小提琴

于 2015-11-19T04:36:11.093 回答
2

这个怎么样:

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

我有类似的问题(在 postgresql 上)和 1M 记录表。这个解决方案需要 1.7 秒,而使用 LEFT JOIN 的解决方案需要 44 秒。在我的情况下,我必须根据 NULL 值过滤您的name字段的对应项,从而使性能提高 0.2 秒

于 2016-11-30T10:50:40.230 回答
2

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

在最后的列上将其设置为 true,并在每行插入/更新/删除时对其进行维护。写入会更慢,但你会从读取中受益。这取决于您的用例,我仅在您专注于阅读时才推荐它。

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

SELECT * FROM Messages WHERE IsLastInGroup = 1
于 2018-05-02T15:05:59.117 回答
2

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_columns
FROM t
GROUP BY name;

db<>小提琴演示

于 2021-04-06T20:58:26.840 回答
2

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

前任。您有一个电影列表,需要获取该系列和最新电影的计数

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

这返回...

ID 系列 姓名
3 星球大战 绝地归来

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

比使用子查询更有效。

所以对于给定的例子:

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

快乐的编码,和“愿原力与你同在”:)

于 2021-08-23T14:45:06.007 回答
1

另一种方法:

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

select * from properties p
join (
    select max(m2_price) as max_price 
    from properties 
    group by program_id
) p2 on (p.program_id = p2.program_id)
having p.m2_price = max_price
于 2019-12-10T08:54:48.970 回答
1

关于什么:

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

我已经在 sqlite 上对其进行了测试,它返回所有名称的所有列和最大 id 值。

于 2020-10-09T19:25:12.647 回答
1

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

SELECT t.*
FROM messages t
JOIN LATERAL (
  SELECT name, MAX(id) AS id 
  FROM messages t1
  WHERE t.name = t1.name
  GROUP BY name
) trn ON t.name = trn.name AND t.id = trn.id

db<>小提琴

于 2021-11-11T10:02:52.810 回答
0
select * from messages group by name desc
于 2016-06-18T14:12:25.363 回答
0

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

SELECT 
    user,
    COUNT(user) AS count,
    MAX(id) as last
FROM request 
GROUP BY user
于 2019-04-07T06:26:53.960 回答
0

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

WITH Temp_table AS
(
    Select id, name, othercolumns, ROW_NUMBER() over (PARTITION BY name ORDER BY ID 
    desc)as rank from messages
)
Select id, name,othercolumns from Temp_table where rank=1
于 2020-01-15T06:07:02.137 回答