EDITED:按请求添加完整查询。mysql - 在连接表列上优化ORDER BY COALESCE
实质上,我有一个帖子的表格,其中链接了一对多的转贴表,类似于Twitter。我想加载在重新发布时(如果有的话)或原始帖子的时间排序的帖子。但是,使用单个查询的排序过程非常缓慢(可能是因为COALESCE(x,y)没有充分利用MySQL索引)。两个相关表格的时间列都被编入索引。
我的查询看起来像这样。
SELECT * FROM Post p LEFT JOIN p.reposts ON ... WHERE ...
ORDER BY COALESCE(r.time, p.time) LIMIT 0, 10
更精确地(伪ISH),因为我使用DAL:
SELECT * FROM Post p LEFT JOIN p.reposts repost ON (p.id = repost.post_id AND
repost.time = (
SELECT MIN(r.time) FROM Repost r WHERE p.id = r.post_id
AND r.user_id IN (1, 2, 3...) AND r.user_id NOT IN (4, 5, 6...))
))
WHERE (repost IS NOT NULL OR p.author_id IN (1, 2, 3...))
AND p.author_id NOT IN (4, 5, 6...)
ORDER BY COALESCE(repost.time, p.time) LIMIT 0, 10
在上文中,ON子句确保至多一个转贴(一个我想)接合。 COALESCE是必要的,因为如果帖子未被转贴,则r可能为NULL。该查询的行为如预期 - 当ORDER BY子句被省略时,或者仅在像p.time这样的索引列上使用时,速度很快。这是预料之中的,因为邮政表是大型的100k +行。
查询说明
编辑:应该做什么查询更好的解释。值得注意的是这里的逻辑起作用 - 我得到了我想要的数据。问题是,应用ORDER BY子句会导致查询运行速度降低大约50倍,因为MySQL无法在连接的表上使用具有COALESCE的索引。
- 加载10个帖子的列表,这些帖子是由一组用户创作的(后面)或由同一集合(后面)转发的,由最近排序的。
- 帖子应按帖子发布时间或第一次转发时间排序。
忽略一组不同的(阻塞)职位和转播用户
获取帖子:从帖子
选择- 由跟随集合中的用户获取最早转贴:LEFT JOIN ON ... r.time =(SELECT MIN(r.time)...)
- 过滤掉未被用户创作或转贴的文章,其中包括:WHERE(转贴不是NULL ...)
- 订购是第一个转载(如果存在)或发布时间:ORDER BY COALESCE(repost.time,p.time)
- 负载最多10个帖子:LIMIT 0,10
UPDATE
我发现:
...ORDER BY repost.time DESC
主要生产见效慢以及除非我还补充:
...WHERE repost.id IS NOT NULL...
在这种情况下,查询速度很快。这使我相信真正的问题是对可空列索引进行排序。我也试过:
... ORDER BY CASE WHEN repost.id IS NULL p.time ELSE repost.time END DESC
哪没有帮助。
更新2
原因在于MySQL使用B树为它的索引的事实,现在看来,这将是不可能以充分利用我想要的方式索引。因此,我目前最好的想法是将每个原始帖子视为其作者的“转贴”,然后在转贴表上执行我的选择和订购,例如,
SELECT * FROM Repost r LEFT JOIN r.post ON ... WHERE ... ORDER BY r.time DESC
“我不会发布我的整个查询,因为它非常复杂。”那么这个练习是毫无意义的。一个不同的查询会有不同的性能问题 – e4c5
如果它有帮助,我可以发布整个事情。但我不认为所有的WHERE和ON都必须相关。我已经剥离并在我自己的测试中省略了各个部分 - 似乎肯定ORDER BY子句和相关的LEFT JOIN导致了痛点。 – CaptainStiggz
基本问题是,按表达式排序需要它生成一个包含所有结果的中间表,以便它可以计算每行的表达式。它不能使用索引来优化它。 – Barmar