检索每组中的最后一条记录 - MySQL
P粉464088437
2023-08-24 15:06:23
<p>There is a table <code>messages</code> that contains data as shown below:</p>
<pre class="brush:php;toolbar:false;">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</pre>
<p>If I run a query <code>select * from messages group by name</code>, I will get the result as:</p>
<pre class="brush:php;toolbar:false;">1 A A_data_1
4 B B_data_1
6 C C_data_1</pre>
<p>什么查询将返回以下结果?</p>
<pre class="brush:php;toolbar:false;">3 A A_data_3
5 B B_data_2
6 C C_data_1</pre>
<p>也就是说,应该返回每组中的最后一条记录。</p>
<p>目前,这是我使用的查询:</p>
<pre class="brush:php;toolbar:false;">SELECT
*
FROM (SELECT
*
FROM messages
ORDER BY id DESC) AS x
GROUP BY name</pre>
<p>但这看起来效率很低。还有其他方法可以达到相同的结果吗?</p>
UPD:2017-03-31,版本5.7.5 MySQL 默认启用 ONLY_FULL_GROUP_BY 开关(因此,非确定性 GROUP BY 查询被禁用)。此外,他们更新了 GROUP BY 实现,即使禁用了开关,该解决方案也可能无法按预期工作。需要检查一下。
Bill Karwin's solution above works fine when item count within groups is rather small, but the performance of the query becomes bad when the groups are rather large, since the solution requires about
n*n/2 + n/2
of onlyIS NULL
comparisons.I made my tests on a InnoDB table of
18684446
rows with1182
groups. The table contains testresults for functional tests and has the(test_id, request_id)
as the primary key. Thus,test_id
is a group and I was searching for the lastrequest_id
for eachtest_id
.Bill's solution has already been running for several hours on my dell e4310 and I do not know when it is going to finish even though it operates on a coverage index (hence
using index
in EXPLAIN).我有几个基于相同想法的其他解决方案:
(group_id, item_value)
pair is the last value within eachgroup_id
, that is the first for eachgroup_id
if we walk through the index in descending order;MySQL 使用索引的 3 种方式 是一篇很棒的文章,可以帮助您了解一些细节。
解决方案1
这个速度快得令人难以置信,在我的 18M+ 行上大约需要 0.8 秒:
如果您想将顺序更改为 ASC,请将其放入子查询中,仅返回 ids 并将其用作子查询来连接其余列:
这对我的数据来说大约需要 1.2 秒。
解决方案2
这是另一个解决方案,对于我的表来说大约需要 19 秒:
它也按降序返回测试。它要慢得多,因为它执行完整索引扫描,但它可以让您了解如何为每个组输出 N 个最大行。
该查询的缺点是查询缓存无法缓存其结果。
MySQL 8.0 现在支持窗口函数,例如几乎所有流行的 SQL 实现。使用这个标准语法,我们可以编写每组最大n个查询:
此方法和其他查找的方法分组最大行数在 MySQL 手册中进行了说明。
以下是我在2009年针对这个问题写的原始答案:
我这样写解决方案:
关于性能,一种解决方案可能会更好,具体取决于数据的性质。因此,您应该测试这两个查询,并根据您的数据库使用性能更好的查询。
For example, I have a copy of the StackOverflow August data dump. I'll use that for benchmarking. There are 1,114,357 rows in the
Posts
table. This is running on MySQL 5.0.75 on my Macbook Pro 2.40GHz.我将编写一个查询来查找给定用户 ID(我的)的最新帖子。
First using the technique shown by @Eric with the
GROUP BY
in a subquery:Even the
EXPLAIN
analysis takes over 16 seconds:Now produce the same query result using my technique with
LEFT JOIN
:The
EXPLAIN
analysis shows that both tables are able to use their indexes:Here's the DDL for my
Posts
table:评论者请注意:如果您想要使用不同版本的 MySQL、不同的数据集或不同的表设计进行另一个基准测试,请随意自己做。我已经展示了上面的技术。 Stack Overflow 在这里向您展示如何进行软件开发工作,而不是为您完成所有工作。