EDITED:按要求添加完整查询。
从本质上讲,我有一个帖子表,一对一地链接到一个转贴表,类似于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
因为我正在使用DAL,所以更准确地说(伪ish):
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子句确保最多连接一个repost(我想要的那个)。 COALESCE是必要的,因为如果帖子没有重新发布,则r可能为NULL。查询的行为与预期相同 - 省略ORDER BY子句时快速,或仅在p.time等索引列上使用。这是预期的,因为Post表大100k +行。
查询说明
编辑:更好地解释查询应该做什么。值得注意的是这里的逻辑是有效的 - 我得到了我想要的数据。问题是应用ORDER BY子句导致查询运行速度减慢约50倍,因为MySQL无法在连接表上使用带COALESCE的索引。
忽略不同组中用户的帖子和重新发布(已屏蔽)
获取帖子:从帖子中选择
更新
我发现:
...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-tree作为其索引,似乎不可能以我想要的方式利用索引。因此,我目前最好的想法是将每个原始帖子作为其作者的“转贴”处理,然后在转贴表上执行我的选择和订单,例如。
SELECT * FROM Repost r LEFT JOIN r.post ON ... WHERE ... ORDER BY r.time DESC
答案 0 :(得分:0)
这里的问题就像我在问题的更新2中描述的那样。 MySQL使用索引快速执行ORDER BY操作。更具体地说,MySQL使用B-trees索引列(例如时间戳 - p.time / r.time),这会占用更多空间但允许更快的排序。
我的查询的问题是它按两个表中的时间列排序,使用重新发布表中的时间戳(如果可用),否则使用post表。由于MySQL无法组合两个表中的B树,因此无法对来自两个不同表的列执行快速索引排序。
我以两种方式修改了我的查询和表结构来解决这个问题。
1)首先根据被阻止的用户执行过滤,因此只需要对当前用户可访问的帖子进行排序。这不是问题的根源,而是实际的优化。 e.g。
SELECT * FROM (SELECT * FROM Post p WHERE p.author_id NOT IN (4, 5, 6...))...
2)将每个帖子视为其作者的转发,因此每个帖子都保证有一个可连接的重新发布和repost.time进行索引和排序。 e.g。
SELECT * FROM (...) 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.id IS NOT NULL) ORDER BY repost.time DESC LIMIT 0, 10
在一天结束时,问题归结为ORDER BY - 这种方法将查询时间从大约8秒减少到20毫秒。