MySql 之 left join 避坑指南

2021-10-06 22:44:19 字數 3261 閱讀 4028

優質文章,第一時間送達

這裡我先給出乙個場景,並丟擲兩個問題,如果你都能答對那這篇文章就不用看了。

select * from classes;

id    name

1 一班

2 二班

3 三班

4 四班

select * from students;

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寫法,請問哪種是對的?

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)
其中p1on過濾條件,缺失則認為是truep2where過濾條件,缺失也認為是true,該語句的執行邏輯可以描述為:

for each row lt in lt 

b=true;// lt在rt中有對應的行

} if (!b)

}}

當然,實際情況中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

需求1由於在where條件中對右表限制,導致資料缺失(四班應該有個為0的結果)

需求2由於在on條件中對左表限制,導致資料多餘(其他班的結果也出來了,還是錯的)

通過上面的問題現象和分析,可以得出了結論:在left join語句中,左表過濾必須放where條件中,右表過濾必須放on條件中,這樣結果才能不多不少,剛剛好。

sql 看似簡單,其實也有很多細節原理在裡面,乙個小小的混淆就會造成結果與預期不符,所以平時要注意這些細節原理,避免關鍵時候出錯。

精彩推薦

好文章,我在看❤️

MySQL 之 LEFT JOIN 避坑指南

假設有乙個班級管理應用,有乙個表classes,存了所有的班級 有乙個表students,存了所有的學生,具體資料如下 create table classes id int 10 unsigned not null auto increment,name varchar 255 default n...

MySQL 之 LEFT JOIN 避坑指南

這裡我先給出乙個場景,並丟擲兩個問題,如果你都能答對那這篇文章就不用看了。假設有乙個班級管理應用,有乙個表classes,存了所有的班級 有乙個表students,存了所有的學生,具體資料如下 那麼現在有兩個需求 找出每個班級的名稱及其對應的女同學數量 找出一班的同學總數 對於需求1,大多數人不假思...

mysql 如何優化left join

今天遇到乙個left join優化的問題,搞了一下午,中間查了不少資料,對mysql的查詢計畫還有查詢優化有了更進一步的了解,做乙個簡單的記錄 select c.from hotel info original c left join hotel info collection h on c.hot...