left join
在咱們使用mysql查詢的過程當中可謂很是常見,好比博客裏一篇文章有多少條評論、商城裏一個貨物有多少評論、一條評論有多少個贊等等。可是因爲對join
、on
、where
等關鍵字的不熟悉,有時候會致使查詢結果與預期不符,因此今天我就來總結一下,一塊兒避坑。mysql
這裏我先給出一個場景,並拋出兩個問題,若是你都能答對那這篇文章就不用看了。sql
假設有一個班級管理應用,有一個表classes,存了全部的班級;有一個表students,存了全部的學生,具體數據以下(感謝廖雪峯的在線SQL):優化
SELECT * FROM classes;
code
id name 1 一班 2 二班 3 三班 4 四班
SELECT * FROM students;
get
id class_id name gender 1 1 小明 M 2 1 小紅 F 3 1 小軍 M 4 1 小米 F 5 2 小白 F 6 2 小兵 M 7 2 小林 M 8 3 小新 F 9 3 小王 M 10 3 小麗 F
那麼如今有兩個需求:博客
對於需求1,大多數人不假思索就能想出以下兩種sql寫法,請問哪一種是對的?class
SELECT c.name, count(s.name) as num FROM classes c left join students s on s.class_id = c.id and s.gender = 'F' group by c.name
或者原理
SELECT c.name, count(s.name) as num FROM classes c left join students s on s.class_id = c.id where s.gender = 'F' group by c.name
對於需求2,大多數人也能夠不假思索的想出以下兩種sql寫法,請問哪一種是對的?循環
SELECT c.name, count(s.name) as num FROM classes c left join students s on s.class_id = c.id where c.name = '一班' group by c.name
或者遍歷
SELECT c.name, count(s.name) as num FROM classes c left join students s on s.class_id = c.id and c.name = '一班' group by c.name
請不要繼續往下翻 !!先給出你本身的答案,正確答案就在下面。
.
.
.
.
.
.
.
.
答案是兩個需求都是第一條語句是正確的,要搞清楚這個問題,就得明白mysql對於left join
的執行原理,下節進行展開。
mysql 對於left join
的採用相似嵌套循環的方式來進行從處理,如下面的語句爲例:
SELECT * FROM LT LEFT JOIN RT ON P1(LT,RT)) WHERE P2(LT,RT)
其中P1
是on
過濾條件,缺失則認爲是TRUE
,P2
是where
過濾條件,缺失也認爲是TRUE
,該語句的執行邏輯能夠描述爲:
FOR each row lt in LT {// 遍歷左表的每一行 BOOL b = FALSE; FOR each row rt in RT such that P1(lt, rt) {// 遍歷右表每一行,找到知足join條件的行 IF P2(lt, rt) {//知足 where 過濾條件 t:=lt||rt;//合併行,輸出該行 } b=TRUE;// lt在RT中有對應的行 } IF (!b) { // 遍歷完RT,發現lt在RT中沒有有對應的行,則嘗試用null補一行 IF P2(lt,NULL) {// 補上null後知足 where 過濾條件 t:=lt||NULL; // 輸出lt和null補上的行 } } }
固然,實際狀況中MySQL會使用buffer的方式進行優化,減小行比較次數,不過這不影響關鍵的執行流程,不在本文討論範圍以內。
從這個僞代碼中,咱們能夠看出兩點:
on
條件中進行,若在where
中進行則可能致使數據缺失,致使左表在右表中無匹配行的行在最終結果中不出現,違背了咱們對left join
的理解。由於對左表無右表匹配行的行而言,遍歷右表後b=FALSE
,因此會嘗試用NULL
補齊右表,可是此時咱們的P2
對右錶行進行了限制,NULL若不知足P2
(NULL
通常都不會知足限制條件,除非IS NULL
這種),則不會加入最終的結果中,致使結果缺失。where
條件,不管on
條件對左表進行怎樣的限制,左表的每一行都至少會有一行的合成結果,對左錶行而言,若右表若沒有對應的行,則右表遍歷結束後b=FALSE
,會用一行NULL
來生成數據,而這個數據是多餘的。因此對左表進行過濾必須用where。下面展開兩個需求的錯誤語句的執行結果和錯誤緣由:
需求1
name num 一班 2 二班 1 三班 2
需求2
name num 一班 4 二班 0 三班 0 四班 0
0
的結果)經過上面的問題現象和分析,能夠得出告終論:在left join
語句中,左表過濾必須放where條件中,右表過濾必須放on條件中,這樣結果才能很少很多,剛恰好。
SQL 看似簡單,其實也有不少細節原理在裏面,一個小小的混淆就會形成結果與預期不符,因此平時要注意這些細節原理,避免關鍵時候出錯。