前言
本文還是秉持之前一貫的寫作風(fēng)格,以簡單易懂的示例幫助大家了解各種join的區(qū)別。
為什么需要join
為什么需要join?join中文意思為連接,連接意味著關(guān)聯(lián)即將一個(gè)表和多個(gè)表之間關(guān)聯(lián)起來。在處理數(shù)據(jù)庫表的時(shí)候,我們經(jīng)常會(huì)發(fā)現(xiàn),需要從多個(gè)表中獲取信息,將多個(gè)表的多個(gè)字段數(shù)據(jù)組裝起來再返回給調(diào)用者。所以join的前提是這些表之間必須有關(guān)聯(lián)字段。
join的分類
join分為兩種,inner join和outer join,其中outer join分為三種,left outer join, right outer join, full outer join,另外left outer join又簡稱為left join即大家所熟知的左連接。
join分為兩種,inner join和outer join,其中outer join分為三種,left outer join, right outer join, full outer join,另外left outer join又簡稱為left join即大家所熟知的左連接。
各種join的區(qū)別
在介紹各種join的區(qū)別之前,我們先來看一個(gè)簡單的示例:
場景描述:
互聯(lián)網(wǎng)時(shí)代,大家都喜歡在網(wǎng)上購物,尤其是淘寶和京東,所以我們選擇的場景也是大家熟悉的網(wǎng)上購物。這是一個(gè)關(guān)于一個(gè)人和他在商城買了什么商品的一個(gè)故事;
針對上述需求,我們建立了兩張表,tb_person和tb_order,其中tb_person是關(guān)于這個(gè)人的描述,tb_order是關(guān)于他購買的商品的一個(gè)描述。
我們的表結(jié)構(gòu)很簡單,tb_person只需要知道這個(gè)人是誰就可以了,所以只有三個(gè)字段id,firstname(名)和lastname(姓),同樣tb_order也很簡單,我們只要知道誰買了什么商品,所以只需要3個(gè)字段,分別是oid, oname(商品名稱), pid(購買者編號(hào))。
tb_person:
+———–+————-+——+—–+———+—————-+
|?Field?????|?Type????????|?Null?|?Key?|?Default?|?Extra??????????|
+———–+————-+——+—–+———+—————-+
|?pid???????|?int(11)?????|?NO???|?PRI?|?NULL????|?auto_increment?|
|?firstname?|?varchar(50)?|?YES??|?????|?NULL????|????????????????|
|?lastname??|?varchar(50)?|?YES??|?????|?NULL????|????????????????|
+———–+————-+——+—–+———+—————-+
tb_order:
+——-+————-+——+—–+———+—————-+
|?Field?|?Type????????|?Null?|?Key?|?Default?|?Extra??????????|
+——-+————-+——+—–+———+—————-+
|?oid???|?int(11)?????|?NO???|?PRI?|?NULL????|?auto_increment?|
|?oname?|?varchar(50)?|?YES??|?????|?NULL????|????????????????|
|?pid???|?int(11)?????|?YES??|?????|?NULL????|????????????????|
+——-+————-+——+—–+———+—————-+
接下來,我們向上述兩張表中寫入一些示例數(shù)據(jù):
data in tb_person:
+—–+———–+———-+
|?pid?|?firstname?|?lastname?|
+—–+———–+———-+
|???1?|?andy??????|?chen?????|
|???2?|?irri??????|?wan??????|
|???3?|?abby??????|?sun??????|
+—–+———–+———-+
tb_person表中有三位人員,分別是andy Chen, irri Wan, abby Sun;
data in tb_order:
+—–+———-+——+
|?oid?|?oname????|?pid??|
+—–+———-+——+
|???1?|?book?????|????1?|
|???2?|?phone????|????1?|
|???3?|?computer?|????4?|
+—–+———-+——+
tb_order表中記錄了3條數(shù)據(jù),人員編號(hào)為1也就是andy Chen買了兩件商品分別是book和phone,另外還有一個(gè)人員編號(hào)為4的人買了一件商品computer。關(guān)于這個(gè)大家可能會(huì)產(chǎn)生疑問,為什么tb_person表中沒有人員編號(hào)為4的人呢?這里我們姑且認(rèn)為由于注冊用戶較多,我們采用了用戶分表策略,所以人員編號(hào)為4的用戶可能在另外一張人員表中。
從之前的描述我們知道,表與表之間如果要join則必須要有關(guān)聯(lián)的字段,上述示例我們看到這個(gè)關(guān)聯(lián)的字段就是pid。
根據(jù)tb_person和tb_order兩張表,我們可以看到有三種情形:
person表中的人購買了商品,也就是order表中有關(guān)于該用戶的商品購買記錄,我們可以從該表中查詢到該用戶買了哪些商品,如andy Chen購買了book和phone兩種商品,即pid在tb_person和tb_order兩種表中都存在;
person表中的人未購買商品,如irri Wan和abby Sun兩位用戶并未購買任何商品,即pid只存在于tb_person表;
order表中購買商品的用戶在person表中找不到記錄,如pid為4的用戶購買了一臺(tái)computer但在tb_person表中沒有該用戶的記錄,即pid只存在于tb_order表;
理解上述三種情形對于我們理解join有非常大的幫助,接下來我們將具體的分析每種join的區(qū)別:
INNER JOIN
所謂inner join的意思就是我們前面提到的情形1,pid必須在tb_person和tb_order兩張表中同時(shí)存在;
MariaDB?[demo]>?SELECT?p.pid,?p.firstname,?o.oname ????->?FROM?tb_person?p ????->?INNER?JOIN?tb_order?o ????->?ON?p.pid=o.pid;
+—–+———–+——-+
|?pid?|?firstname?|?oname?|
+—–+———–+——-+
|???1?|?andy??????|?book??|
|???1?|?andy??????|?phone?|
+—–+———–+——-+
LEFT JOIN
tb_person LEFT JOIN tb_order的意思是上述情形1,情形2的并集。LEFT JOIN的結(jié)果集不僅包含INNER JOIN的結(jié)果,而且還包含所有tb_person中沒有購買任何商品的用戶集。
MariaDB?[demo]>?SELECT?p.pid,?p.firstname,?o.oname ????->?FROM?tb_person?p ????->?LEFT?JOIN?tb_order?o ????->?ON?p.pid=o.pid;
+—–+———–+——-+
|?pid?|?firstname?|?oname?|
+—–+———–+——-+
|???1?|?andy??????|?book??|
|???1?|?andy??????|?phone?|
|???2?|?irri??????|?NULL??|
|???3?|?abby??????|?NULL??|
+—–+———–+——-+
RIGHT JOIN
tb_person RIGHT JOIN tb_order的意思是上述情形1和情形3的并集。RIGHT JOIN的結(jié)果集不僅包含INNER JOIN的結(jié)果,而且還包含所有tb_order中所有已經(jīng)購買商品的用戶但該用戶記錄不存在于tb_person表。
MariaDB?[demo]>?SELECT?p.pid,?p.firstname,?o.oname ????->?FROM?tb_person?p ????->?RIGHT?JOIN?tb_order?o ????->?ON?p.pid=o.pid;
+——+———–+———-+
|?pid??|?firstname?|?oname????|
+——+———–+———-+
|????1?|?andy??????|?book?????|
|????1?|?andy??????|?phone????|
|?NULL?|?NULL??????|?computer?|
+——+———–+———-+
FULL JOIN
故名思議,F(xiàn)ULL JOIN就是上述情形1,2,3的并集了,但是mysql數(shù)據(jù)庫不支持full join查詢,所以我們只能LEFT JOIN union RIGHT JOIN,才能得到FULL JOIN的結(jié)果。
MariaDB?[demo]>?SELECT?p.pid,?p.firstname,?o.oname ????->?FROM?tb_person?p ????->?LEFT?JOIN?tb_order?o ????->?ON?p.pid=o.pid ????->?UNION ????->?SELECT?p.pid,?p.firstname,?o.oname ????->?FROM?tb_person?p ????->?RIGHT?JOIN?tb_order?o ????->?ON?p.pid=o.pid;
+——+———–+———-+
|?pid??|?firstname?|?oname????|
+——+———–+———-+
|????1?|?andy??????|?book?????|
|????1?|?andy??????|?phone????|
|????2?|?irri??????|?NULL?????|
|????3?|?abby??????|?NULL?????|
|?NULL?|?NULL??????|?computer?|
+——+———–+———-+
注:我們上述的sql語句全部基于mysql數(shù)據(jù)庫執(zhí)行。
總結(jié)
本文主要描述了sql join的分類以及各種join的區(qū)別,通過簡單的示例,讓大家更清晰的去了解他們。至于什么時(shí)候使用join要視具體的情況而定,根據(jù)不同的需求采用不同的策略。
非常感謝大家的熱心回復(fù),可能有些問題的探討超出了本文的范疇,但是非常樂意大家提出問題,然后大家一起去探索去發(fā)現(xiàn)。
引用
NULL
附件
demo.sql文件
create?database?demo; use?demo; ? create?table?tb_person?( ????pid?int(11)?auto_increment, ????firstname?varchar(50), ????lastname?varchar(50), ????primary?key(pid) ); ? create?table?tb_order?( ????oid?int(11)?auto_increment, ????oname?varchar(50), ????pid?int(11), ????primary?key(oid) ); ? insert?into?tb_person(firstname,?lastname)?values('andy','chen'); insert?into?tb_person(firstname,?lastname)?values('irri','wan'); insert?into?tb_person(firstname,?lastname)?values('abby','sun'); ? insert?into?tb_order(oname,?pid)?values('book',?1); insert?into?tb_order(oname,?pid)?values('phone',?1); insert?into?tb_order(oname,?pid)?values('computer',?4);