解決Mysql的left join無(wú)效及使用的注意事項(xiàng)說(shuō)明
Mysql的left join無(wú)效及使用
今天寫sql發(fā)現(xiàn)使用left join 沒(méi)有把左邊表的數(shù)據(jù)全部查詢出來(lái),讓我郁悶了一會(huì),后來(lái)仔細(xì)研究了一會(huì)才知道自己犯了個(gè)常識(shí)性的錯(cuò)誤(我是菜鳥(niǎo))
這是原sql
這樣的查詢并不能將tb_line這張表的數(shù)據(jù)都查詢出來(lái),好尷尬...
后面我才知道原來(lái)當(dāng)我們進(jìn)行多表查詢,在執(zhí)行到where之前,會(huì)先形成一個(gè)臨時(shí)表
而on就是臨時(shí)表中的條件篩選,使用left join則不管條件是否為真,都會(huì)查詢出左邊表的數(shù)據(jù),條件為假的,則顯示為null
where則是在臨時(shí)表生成之后的過(guò)濾條件
在第一張圖中,我將tb_vehicle這張表的過(guò)濾條件放在where之中,那left join所產(chǎn)生條件為假的數(shù)據(jù),則會(huì)在where 的 v.del_flag='0'中被過(guò)濾掉(因?yàn)闂l件為假的數(shù)據(jù),del_flag都為空)
所以我看似使用了left join ,實(shí)際上這樣寫與使用inner join的結(jié)果是一樣的
正確sql如下:
在臨時(shí)表中就做好條件篩選,這樣就能夠得到左邊表的數(shù)據(jù)
總結(jié):
使用left join 并需要做條件查詢的時(shí)候,需要仔細(xì)斟酌改條件篩選放在on后面還是where后面
Mysql left join 避坑指南
現(xiàn)象
left join在我們使用mysql查詢的過(guò)程中可謂非常常見(jiàn),比如博客里一篇文章有多少條評(píng)論、商城里一個(gè)貨物有多少評(píng)論、一條評(píng)論有多少個(gè)贊等等。但是由于對(duì)join、on、where等關(guān)鍵字的不熟悉,有時(shí)候會(huì)導(dǎo)致查詢結(jié)果與預(yù)期不符,所以今天我就來(lái)總結(jié)一下,一起避坑。
這里我先給出一個(gè)場(chǎng)景,并拋出兩個(gè)問(wèn)題,如果你都能答對(duì)那這篇文章就不用看了。
假設(shè)有一個(gè)班級(jí)管理應(yīng)用,有一個(gè)表classes,存了所有的班級(jí);有一個(gè)表students,存了所有的學(xué)生,具體數(shù)據(jù)如下:
SELECT * FROM classes;
SELECT * FROM students;
那么現(xiàn)在有兩個(gè)需求:
找出每個(gè)班級(jí)的名稱及其對(duì)應(yīng)的女同學(xué)數(shù)量
找出一班的同學(xué)總數(shù)
對(duì)于需求1,大多數(shù)人不假思索就能想出如下兩種sql寫法,請(qǐng)問(wèn)哪種是對(duì)的?
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
對(duì)于需求2,大多數(shù)人也可以不假思索的想出如下兩種sql寫法,請(qǐng)問(wèn)哪種是對(duì)的?
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
請(qǐng)不要繼續(xù)往下翻 ??!先給出你自己的答案,正確答案就在下面。
~
~
~
答案是兩個(gè)需求都是第一條語(yǔ)句是正確的,要搞清楚這個(gè)問(wèn)題,就得明白mysql對(duì)于left join的執(zhí)行原理,下節(jié)進(jìn)行展開(kāi)。
根源
mysql 對(duì)于left join的采用類似嵌套循環(huán)的方式來(lái)進(jìn)行從處理,以下面的語(yǔ)句為例:
SELECT * FROM LT LEFT JOIN RT ON P1(LT,RT)) WHERE P2(LT,RT)
其中P1是on過(guò)濾條件,缺失則認(rèn)為是TRUE,P2是where過(guò)濾條件,缺失也認(rèn)為是TRUE,該語(yǔ)句的執(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 過(guò)濾條件 t:=lt||rt;//合并行,輸出該行 } b=TRUE;// lt在RT中有對(duì)應(yīng)的行 } IF (!b) { // 遍歷完RT,發(fā)現(xiàn)lt在RT中沒(méi)有有對(duì)應(yīng)的行,則嘗試用null補(bǔ)一行 IF P2(lt,NULL) {// 補(bǔ)上null后滿足 where 過(guò)濾條件 t:=lt||NULL; // 輸出lt和null補(bǔ)上的行 } } }
當(dāng)然,實(shí)際情況中MySQL會(huì)使用buffer的方式進(jìn)行優(yōu)化,減少行比較次數(shù),不過(guò)這不影響關(guān)鍵的執(zhí)行流程,不在本文討論范圍之內(nèi)。
從這個(gè)偽代碼中,我們可以看出兩點(diǎn):
如果想對(duì)右表進(jìn)行限制,則一定要在on條件中進(jìn)行,若在where中進(jìn)行則可能導(dǎo)致數(shù)據(jù)缺失,導(dǎo)致左表在右表中無(wú)匹配行的行在最終結(jié)果中不出現(xiàn),違背了我們對(duì)left join的理解。因?yàn)閷?duì)左表無(wú)右表匹配行的行而言,遍歷右表后b=FALSE,所以會(huì)嘗試用NULL補(bǔ)齊右表,但是此時(shí)我們的P2對(duì)右表行進(jìn)行了限制,NULL若不滿足P2(NULL一般都不會(huì)滿足限制條件,除非IS NULL這種),則不會(huì)加入最終的結(jié)果中,導(dǎo)致結(jié)果缺失。
如果沒(méi)有where條件,無(wú)論on條件對(duì)左表進(jìn)行怎樣的限制,左表的每一行都至少會(huì)有一行的合成結(jié)果,對(duì)左表行而言,若右表若沒(méi)有對(duì)應(yīng)的行,則右表遍歷結(jié)束后b=FALSE,會(huì)用一行NULL來(lái)生成數(shù)據(jù),而這個(gè)數(shù)據(jù)是多余的。所以對(duì)左表進(jìn)行過(guò)濾必須用where。
下面展開(kāi)兩個(gè)需求的錯(cuò)誤語(yǔ)句的執(zhí)行結(jié)果和錯(cuò)誤原因:
需求1
需求2
需求1由于在where條件中對(duì)右表限制,導(dǎo)致數(shù)據(jù)缺失(四班應(yīng)該有個(gè)為0的結(jié)果)
需求2由于在on條件中對(duì)左表限制,導(dǎo)致數(shù)據(jù)多余(其他班的結(jié)果也出來(lái)了,還是錯(cuò)的)
總結(jié)
通過(guò)上面的問(wèn)題現(xiàn)象和分析,可以得出了結(jié)論:在left join語(yǔ)句中,左表過(guò)濾必須放where條件中,右表過(guò)濾必須放on條件中,這樣結(jié)果才能不多不少,剛剛好。
SQL 看似簡(jiǎn)單,其實(shí)也有很多細(xì)節(jié)原理在里面,一個(gè)小小的混淆就會(huì)造成結(jié)果與預(yù)期不符,所以平時(shí)要注意這些細(xì)節(jié)原理,避免關(guān)鍵時(shí)候出錯(cuò)。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 圖文詳解Mysql使用left?join寫查詢語(yǔ)句執(zhí)行很慢問(wèn)題的解決
- mysql使用left?join連接出現(xiàn)重復(fù)問(wèn)題的記錄
- MySQL中多個(gè)left?join?on關(guān)聯(lián)條件的順序說(shuō)明
- 關(guān)于mysql?left?join?查詢慢時(shí)間長(zhǎng)的踩坑總結(jié)
- MYSQL Left Join優(yōu)化(10秒優(yōu)化到20毫秒內(nèi))
- mysql left join快速轉(zhuǎn)inner join的過(guò)程
- mysql高效查詢left join和group by(加索引)
- 詳解mysql 使用left join添加where條件的問(wèn)題分析
- mysql中l(wèi)eft join設(shè)置條件在on與where時(shí)的用法區(qū)別分析
- MySQL 8.0.18 Hash Join不支持left/right join左右連接問(wèn)題
- MySQL left join操作中on和where放置條件的區(qū)別介紹
- mysql多個(gè)left join連接查詢用法分析
- MySQL利用profile分析慢sql詳解(group left join效率高于子查詢)
- MySQL在右表數(shù)據(jù)不唯一的情況下使用left join的方法
- MySQL表LEFT JOIN左連接與RIGHT JOIN右連接的實(shí)例教程
- mysql left join的基本用法以及on與where的區(qū)別
相關(guān)文章
win10下MySQL 8.0登錄Access denied for user‘root’@‘localhost’ (u
這篇文章主要介紹了win10下MySQL 8.0登錄Access denied for user‘root’@‘localhost’ (using password: YES)問(wèn)題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03MySQL中distinct和group?by去重效率區(qū)別淺析
distinct 與 group by均可用于去重,下面這篇文章主要給大家介紹了關(guān)于MySQL中distinct和group?by去重效率區(qū)別的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03Ubuntu Server 16.04下mysql8.0安裝配置圖文教程
這篇文章主要為大家詳細(xì)介紹了Ubuntu Server 16.04下mysql8.0安裝配置圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05MySQL query_cache_type 參數(shù)與使用詳解
這篇文章主要介紹了MySQL query_cache_type參數(shù)介紹,需要的朋友可以參考下2021-07-07mysql中g(shù)rant?all?privileges?on賦給用戶遠(yuǎn)程權(quán)限方式
這篇文章主要介紹了mysql中g(shù)rant?all?privileges?on賦給用戶遠(yuǎn)程權(quán)限方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04