SELECT m.*, pc.call_date
FROM messages m
LEFT JOIN customers c ON m.device_user_id = c.device_user_id
LEFT JOIN phone_call pc ON pc.id = (
SELECT MAX(pc2.id)
FROM phone_call pc2
WHERE pc2.device_user_id = c.device_user_id OR pc2.customer_id = c.customer_id
)
上面的问题是使用left join phone\u call表来查找每个记录的最新通话。电话呼叫表有GB的数据。使用left join phone\u call时,返回数据需要30秒以上。不到一秒钟。所以那张table才是问题所在。有没有更好的方法来实现与上述查询相同的结果?
4条答案
按热度按时间yhived7q1#
由于或条件,max子查询无法使用索引。将此子查询拆分为两个-每个条件一个-并使用
GREATEST()
:每个子查询都需要自己的索引
如果
phone_call.id
是主键,并且表正在使用innodb,那么您可以从索引中omnit它,因为它将被隐式地追加。因为其中一个子查询可能返回
NULL
你应该使用COALESCE()
数字小于任何现有id。如果id
是AUTO_INCREMENT
那么0
应该没问题:7y4bm7vi2#
在mysql 5.7中,您对查询的措辞对我来说很好。但是
OR
在子查询中是性能杀手。我建议使用以下索引,以便快速执行相关子查询:
您可以尝试切换索引中的前两列,以查看某个版本是否有更好的效果。
您可以尝试的另一件事是将子查询更改为使用sort和row limiting子句,而不是聚合(使用相同的上述索引)。可以保证它会改善情况,但值得一试:
最后,另一个想法是将子查询一分为二,以避免
OR
:或无中间聚合:
对于最后两个查询,需要两个索引:
编辑
上述解决方案使用
union all
需要MySQL8.0—在早期版本中,它们失败是因为子查询嵌套太深,无法引用外部查询中的列。所以,另一种选择是IN
:这也可以与
EXISTS
-我更喜欢它,因为 predicate 显式地匹配索引定义,所以mysql应该很容易决定使用它们:同样,这是在假设您有以下两个多列索引的情况下工作的:
您可以按如下方式创建索引:
qyzbxkaa3#
好吧,你可能不喜欢这个答案,但是,如果这是一个重要的数据和一个频繁的查询,我会把
last_call_date
作为客户表中的字段。ryevplcw4#
我相信你的问题与每个组的最大n个问题有关。根据你的分组标准,有几种方法可以获得最新的记录。其中之一是使用自连接,您可以将查询重写为
在上面的查询where子句对于过滤出日期较旧的行很重要,您还需要在上面添加一个复合索引
phone_call
table如果列不构成索引最左侧的前缀,查询优化器将无法使用索引执行查找。
此外,请为您的查询执行explain plan以查看与性能相关的问题,并确保使用了正确的索引。
检索每个组中的最后一条记录-mysql