1. 程式人生 > >in 型子查詢引出的陷阱

in 型子查詢引出的陷阱

在開發商城的時候,核心有一個goods表和category表,category中有多級分類。假設有一個父分類為6,這個父分類中沒有商品,商品都在子分類中,那麼要查詢分類為6的商品,如果我們使用in型子查詢,會使用下面的sql。

select * from  goods where cat_id in (select
cat_id from category where parent_id=6);

直觀的感覺:先執行括號裡面的,也就是select cat_id from category where parent_id = 6,然後在執行select * from goods where cat_id in (1,2,3,4)

,這裡假設6號欄目下有子欄目1,2,3,4。

事實上,並不是這樣的,會全部掃描goods表,每掃到一行,就與category表對照,看parent_id=6是否成立,原因mysql的查詢優化器,針對In型做優化,被改成了exists子查詢的執行效果,當goods表越大時, 查詢速度越慢。

這裡寫圖片描述

我們來看一下查詢計劃,來佐證我的推論,首先在外層sql,在外層中type為all,表示進行全盤查goods表,雖然我已經在cat_id列上加了索引,但是不會使用到任何索引;然後看一下內層sql,在內容sql中使用到了category表的主鍵索引,為什麼呢?因為在外層sql每查詢一條記錄,就會帶上cat_id去cat表中執行這樣的sql,select * from category where cat_id = x and parent_id = 6;

,這個cat_id就是外層sql每查詢到一件商品的分類編號,這條語句會使用到category表的主鍵索引。

這裡寫圖片描述

那麼該如何優化呢?

我們可以改用連線查詢,看看explain分析的結果,首先只是全表掃描category表,而category表的資料比較少,全表掃描對效能的影響也不會太大,不想上面的全表掃描goods,這個對效能的影響就太大了。然後就是連線查詢了,還用到了goods表的cat_id的索引

select * from goods inner join (select cat_id from category where parent_id = 6) as tmp on goods.cat_id = tmp.cat_id;

這裡寫圖片描述

in型子查詢和連線查詢的對比:

這裡寫圖片描述

query_id為1的是in型子查詢,花費時間為0.00131675,而連線查詢的花費時間為0.00068575,可以看到,在這個問題上,連線查詢比in型子查詢來的高效。但並不是說對於所有的查詢in型子查詢都比連線查詢來的慢。