在開發商城的時候,核心有一個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型子查詢都比連接查詢來的慢。