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


952

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

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


2
有关更有效的解决方案,请参见stackoverflow.com/questions/1379565/…中可接受的答案
eyaler 2012年


7
您为什么不能只添加DESC,即从消息组中按名称DESC选择*
Kim Prince


2
@KimPrince似乎您建议的答案与预期不符!我只是尝试了您的方法,所以每个组都花了第一行并命令了DESC。它不会占用每个组的最后一行
Ayrat

Answers:


966

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;

8
真?如果您有大量条目怎么办?例如,如果您正在使用内部版本控制,而每个文件有大量版本,则合并结果将非常庞大。您是否曾经用这个基准测试过子查询方法?我很想知道哪个会赢,但又好奇到不愿意先问您。
Eric

2
做了一些测试。在一个小表上(约30万条记录,约19万个组,因此不是大规模的组或其他任何东西),查询被捆绑在一起(每个8秒)。
埃里克

1
@BillKarwin:请参阅meta.stackexchange.com/questions/123017,尤其是Adam Rackis的回答下方的评论。如果您想重新回答新问题,请告诉我。
罗伯特·哈维

3
@Tim,不,<=如果您的列不唯一,将无济于事。您必须使用唯一列作为决胜局。
比尔·卡温

2
当行数增加或组变大时,性能将呈指数下降。例如,由5个日期组成的组将通过左联接产生4 + 3 + 2 + 1 + 1 = 11行,最后过滤掉其中的一行。与分组结果结合的性能几乎是线性的。您的测试看起来有缺陷。
Salman A

145

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带有1182组的InnoDB 行表上进行了测试。该表包含功能测试的测试结果,并(test_id, request_id)以键为主键。因此,test_id是一个小组,我正在request_id为每个小组寻找最后一个test_id

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

我还有一些基于相同思想的其他解决方案:

  • 如果基础索引是BTREE索引(通常是这种情况),则最大的(group_id, item_value)对是each中的最后一个值group_idgroup_id如果我们按降序浏览索引,则是每个对的第一个值;
  • 如果我们读取索引覆盖的值,则将按照索引的顺序读取值;
  • 每个索引都隐式包含附加到该索引的主键列(即主键在coverage索引中)。在下面的解决方案中,我直接操作主键,在这种情况下,您只需要在结果中添加主键列即可。
  • 在许多情况下,以所需的顺序在子查询中收集所需的行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个最大行。

查询的缺点是查询缓存无法缓存其结果。


请链接到表的转储,以便人们可以在其平台上对其进行测试。
Pacerier

3
解决方法1不能工作,则无法选择REQUEST_ID,而无需在group by子句,
吉奥

2
@giò,这是5岁的答案。直到MySQL的5.7.5 ONLY_FULL_GROUP_BY被默认禁用,这个解决方案工作的开箱dev.mysql.com/doc/relnotes/mysql/5.7/en/...。现在,我不确定禁用该模式时该解决方案是否仍然有效,因为GROUP BY的实现已更改。
newtover

如果您想在第一个解决方案中使用ASC,那么将MAX改为MIN是否可以使用?

@JinIzzraeel,默认情况下,每个组的顶部都有MIN(这是覆盖索引的顺序):SELECT test_id, request_id FROM testresults GROUP BY test_id;将为每个test_id返回最小的request_id。
newtover

101

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

尝试这个:

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

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


1
请注意以下解决方案的注意事项other_col:如果该列不是唯一的name,那么如果它们并列,您可能会获得多个具有相同记录的记录max(other_col)。我发现这篇文章描述了满足我的需求的解决方案,其中每条记录我只需要一条记录name
埃里克·西蒙顿

在某些情况下,您只能使用此解决方案,而不能使用已接受的解决方案。
tom10271

以我的经验,这是对整个该死的消息表进行分组的过程,该表往往很慢/效率很低!换句话说,请注意,子查询需要进行全表扫描,并对其进行分组才能启动...除非您的优化程序正在执行我的不是的事情。因此,此解决方案在很大程度上取决于将整个表保存在内存中。
Timo '18

那些将受益于INDEX(name, id)INDEX(name, other_col)
里克·詹姆斯

55

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

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

与其他一些解决方案相比,我不知道它的性能如何,但是对于我的3+百万行的表来说,它的表现非常出色。(4秒执行,获得1200多个结果)

这在MySQL和SQL Server上均应起作用。


只要确保您在(名称,ID)上有一个索引即可。
塞缪尔·奥斯隆(SamuelÅslund),2016年

1
自我加入要好得多
anwerj

我从您那里学到了一些不错的信息,而且查询速度更快
Humphrey

33

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

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。


1
@AlexanderSuraphel mysql5.5现在在小提琴中不可用,小提琴链接是使用它创建的。现在有一天,小提琴手支持mysql5.6,我将数据库更改为mysql 5.6,并且我能够构建模式并运行sql。
Vipin

8

具有相当大的速度的方法如下。

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

这假设id您已按需要订购。在一般情况下,需要另一些列。
瑞克·詹姆斯

6

这里有两个建议。首先,如果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
)

换句话说,选择没有相同名称的after-Id消息的消息。


8
MySQL不支持ROW_NUMBER()或CTE。
比尔·卡文

1
MySQL 8.0(和MariaDB 10.2)现在支持ROW_NUMBER()和CTE。
里克·詹姆斯

6

我尚未使用大型数据库进行测试,但是我认为这可能比联接表更快:

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

14
这将返回任意数据。换句话说,返回的列可能不是来自具有MAX(Id)的记录。
2014年

从具有WHERE条件的记录集中选择最大ID很有用:“ SELECT Prod WHERE Pn ='” + Pn +“'”从产品中返回最大ID从cn相同的记录集中返回最大ID。使用reader.GetString(0)获得结果
Nicola

5

这是使用GROUP_CONCATorder 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 DESC将以Other_Columns提供的分隔符降序加入所有特定组中的所有分组,在我的情况下||,使用SUBSTRING_INDEX在此列表中使用将选择第一个

小提琴演示


请注意,这group_concat_max_len限制了您可以处理的行数。
瑞克·詹姆斯

5

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

由于此数据是随机的,这意味着多行可以具有相同的recordedTimestamps。我们想要的是按组ID的顺序获取所有选定组的列表,每个组的最后一个记录的时间戳记,如果同一组具有多个匹配行,则该行的最后一个匹配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;

在这种情况下,它只需要检查几百行,因为它不使用任何普通的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;

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


这是一个不同的问题。解决方案是一个巨大的UNION ALL查询。
Paul Spiegel

@PaulSpiegel我猜您在为巨大的UNION ALL开玩笑。除了需要提前知道所有选定组的事实之外,还有2,000个选定组将是一个非常庞大的查询,它的性能甚至比上面最快的示例还要差,所以不,那将不是一个好例子。解。
Yoseph

我是认真的 过去,我已经与数百个小组进行了测试。当您需要处理大团队中的联系时,UNION ALL是MySQL中强制执行最佳执行计划的唯一方法。SELECT DISTINCT(groupID)速度很快,它将为您提供构造此类查询所需的所有数据。您应该使用查询大小,只要它不超过max_allowed_packet,就可以了,在MySQL 5.7中默认为4MB。
Paul Spiegel

5

我们将研究如何使用MySQL获取分组依据中的最后一条记录。例如,如果您有此结果集。

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

参考点击这里


4
SELECT 
  column1,
  column2 
FROM
  table_name 
WHERE id IN 
  (SELECT 
    MAX(id) 
  FROM
    table_name 
  GROUP BY column1) 
ORDER BY column1 ;

您能否详细说明您的答案?为什么您的查询优于Vijays的原始查询?
janfoeh 2014年


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 )  

3

@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

这是我找到的最快的
飞机

3

您也可以从这里欣赏风景。

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 ;

3
SELECT * FROM table_name WHERE primary_key IN (SELECT MAX(primary_key) FROM table_name GROUP BY column_name )


2

有什么方法可以使用此方法删除表中的重复项?结果集基本上是唯一记录的集合,因此,如果我们可以删除结果集中没有的所有记录,那么实际上没有重复项吗?我试过了,但是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)

有没有办法将输出保存到temp变量,然后从NOT IN(temp变量)删除?@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);

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;

2

如果您希望每个行的最后一行Name,则可以按Name和为每个行组指定行号Id降序排列。

查询

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小提琴


2

这个怎么样:

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

我有类似的问题(在postgresql上)和1M记录表上。该解决方案花费了1.7s,而使用LEFT JOIN的解决方案花费了44s。以我为例,我必须根据NULL值过滤您的name字段的对应内容,从而使性能提高了0.2秒


1

如果您确实关心性能,则可以在表上添加一个新列,称为 IsLastInGroup BIT类型。

在最后的列上将其设置为true,并在插入/更新/删除每一行时对其进行维护。写会慢一些,但是读会受益。这取决于您的用例,仅当您着重阅读时,我才建议这样做。

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

SELECT * FROM Messages WHERE IsLastInGroup = 1

Moodle中的某些表具有这样的标志列。
劳伦斯

0
select * from messages group by name desc

0

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

SELECT 
    user,
    COUNT(user) AS count,
    MAX(id) as last
FROM request 
GROUP BY user

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

0

另一种方法:

在每个程序中找到最大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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.