1. 程式人生 > >MYSQL之not in優化方法:left join

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