MYSQL之not in優化方法:left join
MYSQL之not in優化方法:left join
Author:飄易 Source: 飄易正 文:
有一個專案,mysql 語句採用了not in,結果某些頁面開啟需要40多秒,排查sql語句後,發現是採用了 not in 語法導致全表掃描,消耗了大量的時間,飄易記錄下優化的過程:
專案簡介:
會議應該簽到表 signshould :15萬條資料
會議實際簽到表 sign :10萬條資料
請假表 leaves :1000條資料
其中欄位:mid:會議id,uid:使用者id
【例一】:原先的 not in 語句:
select uid from signshould where mid=897 and uid not in(select uid from sign where mid=897 and thetype=0) and uid not in(select uid from leaves where mid=897)
時間: 18.898s
檢視狀態,可以看到 Handler_read_rnd_next 值很大,達到了 1073萬次請求,該引數的含義:在資料檔案中讀下一行的請求數。如果你正進行大量的表掃描,該值較高。通常說明你的表索引不正確或寫入的查詢沒有利用索引。
說明上訴sql語句引起了全表掃描。
explain SQL語句:
此時,我們在 mid 和 uid 上並未建立索引。
優化思路1:在 mid 和 uid 上建立索引後再 explain:
時間下降到: 0.039s。
優化思路2:採用left join 和 右表.id is null 的方法優化
select a.* from signshould as a LEFT JOIN (select * from sign where mid=897 and thetype=0) as b ON a.uid=b.uid LEFT JOIN (select * from leaves where mid=897) as c ON a.uid=c.uid where a.mid=897 and b.uid is null and c.uid is null
沒有建立索引時間: 0.031s
建立索引時間: 0.016s
飄易發現採用 left join 方法執行sql語句的時候:
沒有索引的前提下,執行時間僅為not in方法的 1.6/千分 【0.031/18.898】;
建立了索引後消耗時間僅為not in(也建立索引)方法的 40% 【0.016/0.039】。
索引 | not in 執行時間 | left join 執行時間 | 優化後時間佔比 |
無索引 | 18.898 | 0.031 | 1.6‰ |
有索引 | 0.039 | 0.016 | 40% |
可以看到優化後的 Handler_read_rnd_next 值下降到了22萬。
注:LEFT JOIN 關鍵字會從左表那裡返回所有的行,即使在右表中沒有匹配的行。
EXPLAIN sql:
【例二】:原先的not in的sql語句2:
select uid from sign where mid=674 and thetype=0 and uid not in(select uid from sign where mid=674 and thetype=1) and uid not in(select uid from leaves where mid=674)
時間: 39.208s
可以看到 Handler_read_rnd_next 值很大,達到了 2500萬,也是全表掃描導致的。
採用left join 和 右表.id is null 方法優化:
select a.* from sign as a LEFT JOIN (select * from sign where mid=674 and thetype=1) as b ON a.uid=b.uid LEFT JOIN (select * from leaves where mid=674) as c ON a.uid=c.uid where a.mid=674 and a.thetype=0 and b.uid is null and c.uid is null
時間: 0.048s
優化後 Handler_read_rnd_next 的值下降到了 18萬。
LEFT JOIN 要點:
select t1.id,t2.id from t1 left join t2 on t1.id = t2.id and t1.id>1 and t2.id<>3
在mysql的 left join 中條件放在on後面和在where後面是不同的;
1. on後面只針對於t2表進行過濾,所以上面的 t1.id>1 將不起作用,切記,切記;
2. where後面會對最終結果產生影響,所以如果t2.id<>3放到on後面和where後面也是會返回不同的結果;
例如下面指令碼一會比指令碼二多返回一些資料。
select * from test2 left join test1 on test2.id = test1.id and test1.id<>3 where test2.id <>6; -- select * from test2 left join test1 on test2.id = test1.id where test2.id <>6 and test1.id<>3;
本文完。
作者:飄易
來源:飄易
版權所有。轉載時必須以連結形式註明作者和原始出處及本宣告。
摘自飄易部落格,原文連結:http://www.piaoyi.org/database/MYSQL-not-in-left-join.html#comment-5118