使用MySQL检索每个组的最后一条记录
P粉736935587
2023-08-20 11:48:53
<p>有一个名为<code>messages</code>的表,其中包含如下所示的数据:</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>如果我运行查询<code>select * from messages group by name</code>,将会得到以下结果:</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,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
是一个组,我正在寻找每个test_id
的最后一个request_id
。Bill的解决方案已经在我的戴尔e4310上运行了几个小时,我不知道它何时会完成,尽管它在覆盖索引上操作(因此在EXPLAIN中显示
using index
)。我还有几个基于相同思路的解决方案:
group_id
中的最大(group_id, item_value)
对就是每个group_id
的最后一个值,如果我们按降序遍历索引,则是每个group_id
的第一个值;3 ways MySQL uses indexes是一篇很好的文章,可以了解一些细节。
解决方案1
这个解决方案非常快,对于我1800万+行的数据,大约需要0.8秒:
如果要改变顺序为升序,将其放入子查询中,只返回ID,并将其作为子查询与其他列连接:
对于我的数据,这个解决方案大约需要1.2秒。
解决方案2
这是另一个解决方案,对于我的表,大约需要19秒:
它也按降序返回测试结果。它的速度较慢,因为它进行了完整的索引扫描,但是它可以给你一个关于如何为每个组输出N个最大行的想法。
该查询的缺点是它的结果无法被查询缓存。
MySQL 8.0现在支持窗口函数,几乎所有流行的SQL实现都支持。使用这种标准语法,我们可以编写最大-n-per-group查询:
MySQL手册中展示了此方法及其他寻找分组最大行的方法。
以下是我在2009年为这个问题写的原始答案:
我这样写解决方案:
关于性能,根据数据的性质,其中一种解决方案可能更好。因此,您应该测试两个查询,并根据数据库的性能选择更好的查询。
例如,我有一个StackOverflow八月数据转储的副本。我将用它进行基准测试。在
Posts
表中有1,114,357行数据。这是在我的Macbook Pro 2.40GHz上运行的MySQL 5.0.75。我将编写一个查询来查找给定用户ID(我的)的最新帖子。
首先使用了Eric在子查询中使用
GROUP BY
的技术:即使
EXPLAIN
分析也需要超过16秒:现在使用
LEFT JOIN
使用我的技术产生相同的查询结果:EXPLAIN
分析显示两个表都能使用它们的索引:这是我的
Posts
表的DDL:评论者注意:如果您想要使用不同版本的MySQL、不同的数据集或不同的表设计进行另一个基准测试,请随意自行进行。我已经展示了上述技术。Stack Overflow的目的是向您展示如何进行软件开发工作,而不是为您完成所有工作。