寫在前面的話:
不要求每個人一定理解 聯表查詢(join/left join/inner join等)時的mysql運算過程;
不要求每個人一定知道線上(現在或未來)哪張表數據量大,哪張表數據量小;
但把mysql客戶端(如SQLyog,如HeidiSQL)放在桌面上,時不時拿出來 explain 一把,這是一種美德!
在實例講解之前,我們先回顧一下聯表查詢的基礎知識。
——聯表查詢的基礎知識——
引子:為什么第一個查詢using temporary,第二個查詢不用臨時表呢?
下面兩個查詢,它們只差了一個order by,效果卻迥然不同。
第一個查詢:
EXPLAIN extended
SELECT ads.id
FROM ads, city
WHERE
city.city_id = 8005
AND ads.status = 'online'
AND city.ads_id=ads.id
ORDER BY ads.id desc
執行計划為:
id select_type table type possible_keys key key_len ref rows filtered Extra
------ ----------- ------ ------ -------------- ------- ------- -------------------- ------ -------- -------------------------------
1 SIMPLE city ref ads_id,city_id city_id 4 const 2838 100.00 Using temporary; Using filesort
1 SIMPLE ads eq_ref PRIMARY PRIMARY 4 city.ads_id 1 100.00 Using where
第二個查詢:
EXPLAIN extended
SELECT ads.id
FROM ads,city
WHERE
city.city_id =8005
AND ads.status = 'online'
AND city.ads_id=ads.id
ORDER BY city.ads_id desc
------ ----------- ------ ------ -------------- ------- ------- -------------------- ------ -------- ---------------------------
1 SIMPLE city ref ads_id,city_id city_id 4 const 2838 100.00 Using where; Using filesort
1 SIMPLE ads eq_ref PRIMARY PRIMARY 4 city.ads_id 1 100.00 Using where
1)指定了聯接條件時, 滿足查詢條件的記錄行數少的表為[驅動表];
2) 未指定聯接條件時,行數少的表為[驅動表] (Important!)。
永遠用小結果集驅動大結果集 (Important!)!
——實例講解——
explainSELECT mb.id, ……FROMmb LEFT JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uidWHERE 1=1ORDER BY mbei.apply_time DESClimit 0,10
------ ----------- ------ ------ -------------- -------------- ------- ------------------- ------- --------------------------------------------
1 SIMPLE mb index userid userid 4 (NULL) 6060455 Using index; Using temporary; Using filesort
1 SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index
explainSELECT mb.id……
FROM mb JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uidWHERE 1=1ORDER BY mbei.apply_time DESClimit 0,10
------ ----------- ------ ------ -------------- ------- ------- ---------------------------- ------ --------------
1 SIMPLE mbei ALL mb_id (NULL) (NULL) (NULL) 13383 Using filesort
1 SIMPLE mb eq_ref PRIMARY,userid PRIMARY 4 mbei.mb_id 1
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index
優化第一步之分支1:根據驅動表的字段排序,好嗎?
explainSELECT mb.id……
FROM mb LEFT JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uidWHERE 1=1ORDER BY mb.id DESClimit 0,10
------ ----------- ------ ------ -------------- -------------- ------- ------------------- ------ -----------
1 SIMPLE mb index userid PRIMARY 4 (NULL) 10
1 SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1 Using index
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index
優化第二步:去除所有JOIN,讓MySQL自行決定!
explainFROM mb,mbei,uSELECT mb.id……
WHERE
mb.id=mbei.mb_id
and mb.uid=u.user_id
order by mbei.apply_time desc
limit 0,10
------ ----------- ------ ------ -------------- ------- ------- ---------------------------- ------ --------------
1 SIMPLE mbei ALL mb_id (NULL) (NULL) (NULL) 13388 Using filesort
1 SIMPLE mb eq_ref PRIMARY,userid PRIMARY 4 mbei.mb_id 1
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index
最后的總結:
不要過於相信你的運氣!不要相信你的開發環境里SQL的執行速度!請拿起 explain 武器,如果你看到以下現象,請優化:
- 出現了Using temporary;
- rows過多,或者幾乎是全表的記錄數;
- key 是 (NULL);
- possible_keys 出現過多(待選)索引。
記住,explain 是一種美德!
