MySQL之LEFT JOIN中使用ON和WHRERE對表數(shù)據

背景

left join在我們使用mysql查詢的過程中可謂非常常見,比如博客里一篇文章有多少條評論撇他、商城里一個貨物有多少評論茄猫、一條評論有多少個贊等等。但是由于對join困肩、on划纽、where等關鍵字的不熟悉,有時候會導致查詢結果與預期不符锌畸,所以今天我就來總結一下勇劣,一起避坑。

這里我先給出一個場景潭枣,并拋出兩個問題比默,如果你都能答對那這篇文章就不用看了。

假設有一個班級管理應用盆犁,有一個表classes命咐,存了所有的班級;有一個表students谐岁,存了所有的學生醋奠,具體數(shù)據如下(感謝廖雪峰的在線SQL):

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

那么現(xiàn)在有兩個需求:

  1. 找出每個班級的名稱及其對應的女同學數(shù)量
  2. 找出一班的同學總數(shù)

對于需求1,大多數(shù)人不假思索就能想出如下兩種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,大多數(shù)人也可以不假思索的想出如下兩種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

請不要繼續(xù)往下翻 B肺亍迷捧!先給出你自己的答案,正確答案就在下面胀葱。
.
.
.
.
.
.
.
.
答案是兩個需求都是第一條語句是正確的漠秋,要搞清楚這個問題,就得明白mysql對于left join的執(zhí)行原理抵屿,下節(jié)進行展開庆锦。

根源

mysql 對于left join的采用類似嵌套循環(huán)的方式來進行從處理,以下面的語句為例:

SELECT * FROM LT LEFT JOIN RT ON P1(LT,RT)) WHERE P2(LT,RT)

其中P1on過濾條件轧葛,缺失則認為是TRUE搂抒,P2where過濾條件艇搀,缺失也認為是TRUE,該語句的執(zhí)行邏輯可以描述為:

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,發(fā)現(xiàn)lt在RT中沒有有對應的行芳杏,則嘗試用null補一行
    IF P2(lt,NULL) {// 補上null后滿足 where 過濾條件
      t:=lt||NULL; // 輸出lt和null補上的行
    }         
  }
}

當然矩屁,實際情況中MySQL會使用buffer的方式進行優(yōu)化,減少行比較次數(shù)爵赵,不過這不影響關鍵的執(zhí)行流程吝秕,不在本文討論范圍之內。

從這個偽代碼中空幻,我們可以看出兩點:

  1. 如果想對右表進行限制烁峭,則一定要在on條件中進行,若在where中進行則可能導致數(shù)據缺失氛悬,導致左表在右表中無匹配行的行在最終結果中不出現(xiàn)则剃,違背了我們對left join的理解。因為對左表無右表匹配行的行而言如捅,遍歷右表后b=FALSE,所以會嘗試用NULL補齊右表棍现,但是此時我們的P2對右表行進行了限制,NULL若不滿足P2(NULL一般都不會滿足限制條件镜遣,除非IS NULL這種)己肮,則不會加入最終的結果中,導致結果缺失悲关。
  2. 如果沒有where條件谎僻,無論on條件對左表進行怎樣的限制,左表的每一行都至少會有一行的合成結果寓辱,對左表行而言艘绍,若右表若沒有對應的行,則右表遍歷結束后b=FALSE秫筏,會用一行NULL來生成數(shù)據诱鞠,而這個數(shù)據是多余的。所以對左表進行過濾必須用where这敬。

下面展開兩個需求的錯誤語句的執(zhí)行結果和錯誤原因:
需求1

name    num
一班    2
二班    1
三班    2

需求2

name    num
一班    4
二班    0
三班    0
四班    0
  1. 需求1由于在where條件中對右表限制航夺,導致數(shù)據缺失(四班應該有個為0的結果)
  2. 需求2由于在on條件中對左表限制,導致數(shù)據多余(其他班的結果也出來了崔涂,還是錯的)

總結

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

SQL 看似簡單涮俄,其實也有很多細節(jié)原理在里面蛉拙,一個小小的混淆就會造成結果與預期不符,所以平時要注意這些細節(jié)原理彻亲,避免關鍵時候出錯孕锄。

文章轉摘自:https://segmentfault.com/a/1190000020458807

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市苞尝,隨后出現(xiàn)的幾起案子畸肆,更是在濱河造成了極大的恐慌,老刑警劉巖宙址,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件轴脐,死亡現(xiàn)場離奇詭異,居然都是意外死亡抡砂,警方通過查閱死者的電腦和手機大咱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來注益,“玉大人碴巾,你說我怎么就攤上這事〕笊Γ” “怎么了厦瓢?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵啤月,是天一觀的道長煮仇。 經常有香客問我,道長浙垫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮志鹃,結果婚禮上,老公的妹妹穿的比我還像新娘陕见。我一直安慰自己忍坷,他們只是感情好,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布零聚。 她就那樣靜靜地躺著,像睡著了一般蚂会。 火紅的嫁衣襯著肌膚如雪儡司。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天柴钻,我揣著相機與錄音蜡吧,去河邊找鬼毫蚓。 笑死,一個胖子當著我的面吹牛斩跌,可吹牛的內容都是我干的绍些。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼耀鸦,長吁一口氣:“原來是場噩夢啊……” “哼柬批!你這毒婦竟也來了?” 一聲冷哼從身側響起袖订,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤氮帐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后洛姑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體上沐,經...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年楞艾,在試婚紗的時候發(fā)現(xiàn)自己被綠了参咙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡硫眯,死狀恐怖蕴侧,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情两入,我是刑警寧澤净宵,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站裹纳,受9級特大地震影響择葡,放射性物質發(fā)生泄漏。R本人自食惡果不足惜剃氧,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一敏储、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧朋鞍,春花似錦已添、人聲如沸迫横。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至恨狈,卻和暖如春疏哗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背禾怠。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工返奉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吗氏。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓芽偏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親弦讽。 傳聞我的和親對象是個殘疾皇子污尉,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內容