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