有一张table 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
但这看起来效率很低。还有其他方法可以达到同样的效果吗?
11条答案
按热度按时间1l5u6lss1#
mysql 8.0现在支持窗口功能,就像几乎所有流行的sql实现一样。使用此标准语法,我们可以编写最大n个每组查询:
以下是我在2009年为这个问题写的原始答案:
我这样写解:
关于性能,根据数据的性质,一种解决方案或另一种解决方案可能更好。因此,您应该测试这两个查询,并在给定数据库的情况下使用性能更好的查询。
例如,我有一个stackoverflow数据转储的副本。我会用它来做基准测试。有1114357行
Posts
table。这是运行在MySQL5.0.75在我的MacBookPro 2.40ghz。我将编写一个查询来查找给定用户id(我的)的最新文章。
首先使用@eric展示的技术
GROUP BY
在子查询中:即使是
EXPLAIN
分析耗时超过16秒:现在使用我的技术生成相同的查询结果
LEFT JOIN
:这个
EXPLAIN
分析表明,两个表都可以使用它们的索引:这是我的ddl
Posts
表格:eqfvzcg82#
使用子查询返回正确的分组,因为已经完成了一半。
试试这个:
如果不是的话
id
你想要最大值:通过这种方式,您可以避免子查询中的相关子查询和/或排序,这往往非常缓慢/低效。
tzdcorbm3#
子查询fiddle链接解决方案
用连接条件fiddle-link求解
这个职位的原因是给小提琴链接只。其他答案中已经提供了相同的sql。
zf9nrax14#
下面是另一种使用
GROUP_CONCAT
和订单SUBSTRING_INDEX
从列表中选择一条记录上面的查询将对所有
Other_Columns
在同一个地方Name
分组和使用ORDER BY id DESC
将加入所有Other_Columns
在特定的组中按降序排列,在我的例子中使用了分隔符||
,使用SUBSTRING_INDEX
在这张单子上挑第一张小提琴演示
vql8enpb5#
我找到了一个不同的解决方案,即获取每个组中最后一篇文章的id,然后使用第一个查询的结果作为参数从messages表中进行选择
WHERE x IN
构造:我不知道与其他一些解决方案相比,它的性能如何,但是对于我的300多万行的表来说,它的工作非常出色(4秒执行(1200+个结果)
这应该可以在mysql和sqlserver上使用。
lbsnaicq6#
我们将研究如何使用mysql获取一组记录中的最后一条记录。例如,如果你有这个结果集的职位。
身份证类别身份证职务
1 1 Title 1
2 1 Title 2
3 1 Title 34 2 Title 4
5 2 Title 56 3 Title 6
我想能够得到每个类别的最后一个职位,即标题3,标题5和标题6。要按类别获取文章,您将使用mysql group by keyboard。select * from posts group by category_id
但是我们从这个查询中得到的结果是。身份证类别身份证职务
1 1 Title 1
4 2 Title 46 3 Title 6
groupby将始终返回结果集中组中的第一条记录。SELECT id, category_id, post_title FROM posts WHERE id IN ( SELECT MAX(id) FROM posts GROUP BY category_id );
这将返回每个组中ID最高的帖子。身份证类别身份证职务
3 1 Title 3
5 2 Title 56 3 Title 6
参考点击这里jtw3ybtb7#
显然,有很多不同的方法可以得到相同的结果,你的问题似乎是什么是一种有效的方法来获得mysql中每个组的最后一个结果。如果您使用的是大量数据,并且假设您使用的是innodb,甚至是最新版本的mysql(如5.7.21和8.0.4-rc),那么可能没有一种有效的方法来实现这一点。
我们有时需要对超过6000万行的表执行此操作。
对于这些示例,我将使用只有大约150万行的数据,其中查询需要查找数据中所有组的结果。在我们的实际案例中,我们通常需要返回大约2000组的数据(假设不需要检查太多数据)。
我将使用以下表格:
温度表中有大约150万条随机记录,有100个不同的组。所选的组由这100个组填充(在我们的案例中,对于所有组,这通常小于20%)。
由于此数据是随机的,这意味着多行可以具有相同的RecordedTimeStamp。我们想要的是按照groupid的顺序得到所有选定组的列表,每个组的最后一个recordedtimestamp,如果同一个组有多个这样的匹配行,那么这些行的最后一个匹配id。
如果假设mysql有一个last()函数,它以特殊的order by子句返回最后一行的值,那么我们可以简单地执行以下操作:
在这种情况下,它只需要检查几百行,因为它不使用任何正常的groupby函数。这将在0秒内执行,因此效率很高。请注意,通常在mysql中,我们会看到一个order by子句紧跟在group by子句之后,但是这个order by子句用于确定last()函数的顺序,如果它在group by之后,那么它将对组进行排序。如果不存在GROUPBY子句,则所有返回行中的最后值都将相同。
然而mysql没有这样的功能,所以让我们看看它有什么不同的想法,并证明这些都不是有效的。
例1
这检查了3009254行,在5.7.21上花费了约0.859秒,在8.0.4-rc上花费了稍长的时间
例2
这检查了1505331行,在5.7.21上花费了约1.25秒,在8.0.4-rc上花费了稍长的时间
例3
这检查了3009685行,在5.7.21上花费了约1.95秒,在8.0.4-rc上花费了稍长的时间
例4
这检查了6137810行,在5.7.21上花费了约2.2秒,在8.0.4-rc上花费了稍长的时间
例5
这检查了6017808行,在8.0.4-rc上耗时约4.2秒
例6
这个检查
6tr1vspr8#
速度相当快的方法如下。
结果
tkqqtvp19#
我还没有使用大型数据库进行测试,但我认为这可能比连接表更快:
jdgnovmf10#
这里有两条建议。首先,如果mysql支持row\u number(),那么非常简单:
我猜“最后”是指身份证顺序中的最后一个。如果不是,则相应地更改row\ u number()窗口的order by子句。如果行号()不可用,这是另一种解决方案:
其次,如果没有,这通常是一个很好的方法:
换言之,选择没有相同名称的后续id消息的消息。
tpxzln5u11#
upd:2017-03-31,mysql 5.7.5版本默认启用了唯一的\u full \u group \u by开关(因此,禁用了不确定的groupby查询)。此外,他们通过实现更新了组,即使禁用了交换机,解决方案也可能不再按预期工作。一个人需要检查一下。
当组内的项计数非常小时,bill karwin的上述解决方案可以很好地工作,但是当组非常大时,查询的性能会变差,因为该解决方案需要大约
n*n/2 + n/2
仅限IS NULL
比较。我在innodb的
18684446
带的行1182
组。该表包含功能测试的testresults,并具有(test_id, request_id)
作为主键。因此,test_id
是一个小组,我在寻找最后一个request_id
对于每个test_id
.bill的解决方案已经在我的dell e4310上运行了几个小时,我不知道它什么时候才能完成,即使它在覆盖率索引上运行(因此
using index
在解释中)。我有几个基于相同想法的其他解决方案:
如果基础索引是btree index(通常是这种情况),则
(group_id, item_value)
pair是每个group_id
,这是每个group_id
如果我们按降序遍历索引;如果我们读取索引所覆盖的值,则这些值将按索引的顺序读取;
每个索引都隐式地包含附加到该索引的主键列(即主键位于覆盖率索引中)。在下面的解决方案中,我直接对主键进行操作,在您的情况下,您只需要在结果中添加主键列。
在许多情况下,在子查询中按所需顺序收集所需的行id并将子查询的结果连接到id上要便宜得多。因为对于子查询结果中的每一行,mysql都需要一个基于主键的获取,子查询将放在联接中的第一位,行将按照子查询中id的顺序输出(如果我们省略联接的显式order by)
mysql使用索引的3种方法是一篇很好的文章,可以帮助您了解一些细节。
解决方案1
这是一个难以置信的速度,它需要约0.8秒我的18米以上的行:
如果要将顺序更改为asc,请将其放入子查询中,仅返回id,并将其用作子查询以联接到其余列:
这一次我的数据大约需要1,2秒。
解决方案2
下面是另一个需要19秒的解决方案:
它还按降序返回测试。它的速度要慢得多,因为它会执行完整的索引扫描,但这里提供了一个如何为每个组输出n个最大行的想法。
查询的缺点是查询缓存无法缓存其结果。