[英]MySQL statement that was working fine previously all of a sudden started being very slow. It includes alot of LEFT JOINs
MySQL 關於我幾年前創建的學校系統的聲明運行良好,但現在需要將近 30 秒才能提取在我看來是一個簡單的聲明,但無法弄清楚如何改進它。 我想知道是否有人可以幫助我重新編寫此語句以獲得更快的響應。 聲明是:
SELECT es.*,c.mainsubarea AS subject, b.name,b.email,GROUP_CONCAT(doc.document_file SEPARATOR "|") document_file
FROM usersubinfo es
LEFT JOIN userinfo b ON (es.uid=b.uid)
LEFT JOIN lkptsubjectarea c ON (es.mainsubjectarea=c.id)
LEFT JOIN lkptdeliverytime d ON (es.deliverytime = d.id)
LEFT JOIN documents doc ON (es.id = doc.order_id)
WHERE es.id AND es.is_active='Yes'
GROUP BY es.id
ORDER BY es.joindate
DESC LIMIT 0,25
首先,考慮編寫一個有效的 ANSI SQL 聚合查詢,更改您的GROUP BY
和SELECT
子句。 目前,您的查詢僅包括GROUP BY
中的一列,但usersubinfo
表中的所有列都包含SELECT es.*
以及其他非聚合列。 您甚至可以按不在GROUP BY
中的列排序。
此類查詢針對 SQL 標准運行,並且在大多數 RDBMS 中將失敗,但在 MySQL 中允許,因為它的ONLY_FULL_GROUP_BY
模式已關閉,這會危險地允許:
服務器可以從每個組中自由選擇任何值,因此除非它們相同,否則選擇的值是不確定的,這可能不是您想要的
由於您有一個聚合 function, GROUP_CONCAT
,所有其他非聚合列應放在GROUP BY
子句中。 如果您需要在SELECT
中添加列,請將其也添加到GROUP BY
中。 此外,您可能有一個多余的LEFT JOIN
, SELECT
的其他JOIN
或列沒有任何用途。
SELECT es.id,
es.joindate,
sa.mainsubarea AS subject,
i.name,
i.email,
GROUP_CONCAT(doc.document_file SEPARATOR "|") document_file
FROM usersubinfo es
LEFT JOIN userinfo i ON (es.uid = i.uid)
LEFT JOIN lkptsubjectarea sa ON (es.mainsubjectarea = sa.id)
-- LEFT JOIN lkptdeliverytime dlv ON (es.deliverytime = dlv.id) -- POSSIBLY REDUNDANT
LEFT JOIN documents doc ON (es.id = doc.order_id)
WHERE es.id IS NOT NULL
AND es.is_active = 'Yes'
GROUP BY es.id,
es.joindate,
sa.mainsubarea,
i.name,
i.email,
ORDER BY es.joindate DESC
LIMIT 0
OFFSET 25
此外,通過避免SELECT *
您可以避免引入不需要的甚至更新的列,允許索引在大型表掃描中有效運行,並避免通過網絡發送大量內容。 請參閱為什么 SELECT * 被認為是有害的?
添加索引:這些可能會有所幫助:
b: INDEX(uid, name, email)
doc: INDEX(order_id, document_file)
刪除 LEFT:是否有LEFT JOIN
而不是JOIN
的原因? 我想不是。 看看你在沒有LEFTs
的情況下是否得到相同的結果。
刪除虛假測試:為什么WHERE es.id
? 如果id
是es
的PRIMARY KEY
,則該測試將始終為真。
改進 GROUP+ORDER:改變
GROUP BY es.id
ORDER BY es.joindate DESC
LIMIT 0,25
-->
GROUP BY es.joindate, es.id
ORDER BY es.joindate DESC, es.id DESC
LIMIT 0,25
這避免了兩次數據傳遞——一次用於 GROUPing,另一次用於 ORDERing。 同時,我認為我的分組和排序“一樣好”。
由內而外:這帶來了另一個問題,我稱之為“explode-implode”。 這就是你加入很多行的地方,只是為了擺脫其中的大部分。 所以...
首先以盡可能少的努力找到所需的 25 個 ID:
SELECT id
FROM usersubinfo
WHERE is_active = 'Yes'
GROUP BY joindate, id
ORDER BY joindate DESC, id DESC
LIMIT 0,25
並將其作為“派生”表包含在 rest 中:
SELECT es.*, c.mainsubarea AS subject,
b.name, b.email,
GROUP_CONCAT(doc.document_file SEPARATOR "|") document_file
FROM ( put the above Select here
) AS ids
JOIN usersubinfo AS es USING(id)
JOIN userinfo b ON (es.uid=b.uid)
JOIN lkptsubjectarea c ON (es.mainsubjectarea=c.id)
JOIN lkptdeliverytime d ON (es.deliverytime = d.id)
JOIN documents doc ON (es.id = doc.order_id)
ORDER BY joindate DESC, id DESC; -- yes, repeat this
這可能會更快,因為除了usersubinfo
之外的表只會被觸及 25 次。
(我認為這將避免 Parfait 所指的“only_full_group_by”問題。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.