數(shù)據(jù)庫的join查詢

一、數(shù)據(jù)庫的 join 查詢

數(shù)據(jù)庫提供了多種類型的連接方式,它們之間的區(qū)別在于:從相互交疊的不同數(shù)據(jù)集合中選擇用于連接的行時所采用的方法不同。

A.內(nèi)連接

內(nèi)連接,即最常見的等值連接。【兩邊的表都加限制】

B.外連接

  1. 左外連接:左表不加限制,保留左表的數(shù)據(jù),匹配右表,右表沒有匹配到的行中的列顯示為 null。【左外連接就是在等值連接的基礎上加上左表中的未匹配數(shù)據(jù)】
  2. 右外連接:右表不加限制,保留右表的數(shù)據(jù),匹配左表,左表沒有匹配到的行中列顯示為 null。【右外連接就是在等值連接的基礎上加上右表中的未匹配數(shù)據(jù)】
  3. 完全外連接:左右表都不加限制。即結果為:左右表匹配的數(shù)據(jù)+左表沒有匹配到的數(shù)據(jù)+右表沒有匹配到的數(shù)據(jù)。【完全外連接就是在等值連接的基礎上將左表和右表的未匹配數(shù)據(jù)都加上】

連接的語法:【通常外連接省略outer關鍵字】
left/right/full outer join …on
left/right/full join …on
(+)號的作用:+號可以理解為補充的意思,即哪個表有加號,這個表就是匹配表。加在右表的列上代表右表為補充,為左外連接。加在左表的列上代表左表為補充,為右外連接。
注意:完全外連接不支持(+)寫法。

創(chuàng)建兩種表,生出測試數(shù)據(jù):

CREATE TABLE TQA (
id number,
name VARCHAR2(10)
);
CREATE TABLE TUB (
id number,
name VARCHAR2(10)
);
INSERT INTO TQA VALUES(1,‘QA’);
INSERT INTO TQA VALUES(2,‘QB’);
INSERT INTO TQA VALUES(3,‘QC’);
INSERT INTO TQA VALUES(4,‘QD’);
INSERT INTO TQA VALUES(5,‘QE’);
INSERT INTO TUB VALUES(1,‘UA’);
INSERT INTO TUB VALUES(1,‘UB’);
INSERT INTO TUB VALUES(2,‘UC’);
INSERT INTO TUB VALUES(1,‘UD’);
INSERT INTO TUB VALUES(7,‘UE’);

1??左外連接

select * from TQA a left join TUB b on a.id=b.id;
select * from TQA a,TUB b where a.id=b.id(+);

2??右外連接

select * from TQA a right join TUB b on a.id = b.id;
select * from TQA a,TUB b where a.id(+)=b.id;

3??完全外連接

select * from TQA a full join TUB b on a.id=b.id;

4??等值連接(內(nèi)連接也可省略關鍵字inner,直接寫成join)

select * from TQA a,TUB b where a.id=b.id;
select * from TQA a join TUB b on a.id=b.id;~~~~~等值連接也可以這樣寫
select a.name,b.age from TQA a join TUB b using(id)【`using(id)`等價于`on a.id=b.id`】

注意:等值連接和完全外連接是有區(qū)別的。等值連接是只把滿足條件的兩個表的行相連,然后顯示出來。完全外連接是把匹配查詢條件的、左表沒有匹配到的、右表沒有匹配到的行都顯示出來。

二、總結

SQL 連接(inner/outer join)包括以下:

  1. 內(nèi)連接(兩邊的表都加限制)–[inner] join
  2. 左外連接(左邊的表不加限制)–left [outer] join
  3. 右外連接(右邊的表不加限制)–right [outer] join
  4. 全外連接(左右兩表都不加限制)–full [outer] join

在左外連接和右外連接時都會以主表為基礎表,該表的內(nèi)容會全部顯示,然后加上主表和匹配表匹配的內(nèi)容。 如果主表的數(shù)據(jù)在匹配表中沒有記錄,那么在相關聯(lián)的結果集行中列顯示為空值(null)。

內(nèi)連接,可以使用"(+)",但是必須省略。即兩張表均為"主表",都不是匹配表。而對于外連接, 也可以使用“(+) ”來表示。關于外連接使用(+)的一些注意事項:

  1. (+)操作符只能出現(xiàn)在 where 子句中,并且不能與 outer join 語法同時使用。
  2. 當使用(+)操作符執(zhí)行外連接時,如果在 where 子句中包含有多個條件,則必須在所有條件中都包含(+)操作符。
  3. (+)操作符只適用于列,而不能用在表達式上。
  4. (+)操作符不能與 or 和 in 操作符一起使用。
  5. (+)操作符只能用于實現(xiàn)左外連接和右外連接,而不能用于實現(xiàn)完全外連接。

三、注意

left jon on:當 on 條件存在多個時候(left join on ... and ...)會出現(xiàn)一些與預期不符的查詢結果。left join on 多條件失效,會導致主表的記錄全部查出來,and 條件沒有起作用。回顧 left join 的定義,主表會返回所有行,所以 left join 如果對左邊表進行約束的話是不會生效的;但是,對 left join 的右邊表添加條件的話是生效的!反之,right join 同理。

create table a(f1 int, f2 int, index(f1))engine=innodb;
create table b(f1 int, f2 int)engine=innodb;
insert into a values(1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
insert into b values(3,3),(4,4),(5,5),(6,6),(7,7),(8,8);

表 a 和 b 都有兩個字段 f1 和 f2,不同的是表 a 的字段 f1 上有索引。然后,兩個表中都插了 6 條記錄,其中在表 a 和 b 中同時存在的數(shù)據(jù)有 4 行。

1??兩個表 join 包含多個條件的等值匹配,是都要寫到 on 里面,還是只把一個條件寫到 on 里面,其他條件寫到 where 部分?也就是如下 Q1 和 Q2 有何區(qū)別?

select * from a left join b on(a.f1=b.f1) and (a.f2=b.f2);/*Q1*/
select * from a left join b on(a.f1=b.f1) where (a.f2=b.f2);/*Q2*/

這兩個語句的語義邏輯并不相同。二者執(zhí)行結果如下:

可以看到:

  1. Q1 返回的數(shù)據(jù)集是 6 行,表 a 中即使沒有滿足匹配條件的記錄,查詢結果中也會返回一行,并將表 b 的各個字段值填成 NULL。
  2. Q2 返回的數(shù)據(jù)集是 4 行。從邏輯上可以這么理解,最后兩行,由于表 b 中沒有匹配的字段,結果集里面 b.f2 的值是空,不滿足 where 的條件判斷,因此不能作為結果集的一部分。

Q1 的 explain 結果:

Q1 結果符合預期:驅(qū)動表是表 a,被驅(qū)動表是表 b。由于表 b 的 f1 字段上沒有索引,所以使用的是 Block Nexted Loop Join(簡稱BNL) 算法。由此,這條語句的執(zhí)行流程如下:

  1. 把表 a 的內(nèi)容讀入 join_buffer 中。因為是 select *,所以字段 f1 和 f2 都被放入 join_buffer 了。
  2. 順序掃描表 b,對于每一行數(shù)據(jù),判斷 join 條件(也就是a.f1=b.f1 and a.f2=b.f2)是否滿足,滿足條件的記錄,作為結果集的一行返回。如果語句中有 where 子句,需要先判斷 where 部分滿足條件后,再返回。
  3. 表 b 掃描完成后,對于沒有被匹配的表 a 的行(也就是(1,1)、(2,2)這兩行),把剩余字段補上 NULL,再放入結果集中。

對應的流程圖如下:

可以看到,這條語句確實是以表 a 為驅(qū)動表,而且從執(zhí)行效果看,也和使用 straight_join 是一樣的。

2??如果用 left join 的話,左邊的表一定是驅(qū)動表嗎?

語句 Q2 的查詢結果里面少了最后兩行數(shù)據(jù),是不是就是把上面流程中的步驟 3 去掉呢?看下語句 Q2 的 explain 結果:

可以看到,這條語句是以表 b 為驅(qū)動表的。而如果一條 join 語句的 Extra 字段什么都沒寫的話,就表示使用的是 Index Nested-Loop Join(簡稱NLJ)算法。因此,Q2 的執(zhí)行流程:順序掃描表 b,每一行用 b.f1 到表 a 中去查,匹配到記錄后判斷 a.f2=b.f2 是否滿足,滿足條件的話就作為結果集的一部分返回。

Q1 和 Q2 這兩個執(zhí)行流程為什么會有這么大差距?其實,這是因為優(yōu)化器基于 Q2 這個查詢的語義做了優(yōu)化。

Q2 里面 where a.f2=b.f2 就表示,查詢結果里面不會包含 b.f2 是 NULL 的行,如此 Q2 的語義就是“找到這兩個表里面,f1、f2 對應相同的行。對于表 a 中存在,而表 b 中匹配不到的行,就放棄”。所以 Q2 雖然用的是 left join,但是語義跟 join 是一致的。

因此,優(yōu)化器就把這條語句的 left join 改寫成了 join,然后因為表 a 的 f1 上有索引,就把表 b 作為驅(qū)動表,這樣就可以用上 NLJ 算法。在執(zhí)行 explain 之后,再執(zhí)行 show warnings,就能看到這個改寫的結果,如圖:

這個例子說明,即使在 SQL 語句中寫成 left join,執(zhí)行過程還是有可能不是從左到右連接的。也就是說,使用 left join 時,左邊的表不一定是驅(qū)動表。

3??這樣看來,如果需要 left join 的語義,就不能把被驅(qū)動表的字段放在 where 條件里面做等值判斷或不等值判斷,必須都寫在 on 里面。那如果是 join 語句呢?

select * from a join b on(a.f1=b.f1) and (a.f2=b.f2); /*Q3*/
select * from a join b on(a.f1=b.f1) where (a.f2=b.f2);/*Q4*/

執(zhí)行 explain 和 show warnings,看看優(yōu)化器是怎么做的:

可以看到,這兩條語句都被改寫成:

select * from a join b where (a.f1=b.f1) and (a.f2=b.f2);

執(zhí)行計劃自然也是一模一樣的。也就是說,在這種情況下,join 將判斷條件是否全部放在 on 部分就沒有區(qū)別了。

四、Simple Nested Loop Join 的性能問題

join 語句使用不同的算法,對語句的性能影響會很大。雖然 BNL 算法和 Simple Nested Loop Join 算法都是要判斷 M*N 次(M和N分別是join的兩個表的行數(shù)),但是 Simple Nested Loop Join 算法的每輪判斷都要走全表掃描,因此性能上 BNL 算法執(zhí)行起來會快很多。

1??BNL 算法的執(zhí)行邏輯

  1. 首先,將驅(qū)動表的數(shù)據(jù)全部讀入內(nèi)存 join_buffer 中,這里 join_buffer 是無序數(shù)組。
  2. 然后,順序遍歷被驅(qū)動表的所有行,每一行數(shù)據(jù)都跟 join_buffer 中的數(shù)據(jù)進行匹配,匹配成功則作為結果集的一部分返回。

2??Simple Nested Loop Join算法的執(zhí)行邏輯

順序取出驅(qū)動表中的每一行數(shù)據(jù),到被驅(qū)動表去做全表掃描匹配,匹配成功則作為結果集的一部分返回。

Simple Nested Loop Join 算法,其實也是把數(shù)據(jù)讀到內(nèi)存里,然后按照匹配條件進行判斷,為什么性能遠不如 BNL 算法?

解釋這個問題,需要用到 MySQL 中索引結構和 Buffer Pool 的相關知識點:

  1. 在對被驅(qū)動表做全表掃描的時候,如果數(shù)據(jù)沒有在 Buffer Pool 中,就需要等待這部分數(shù)據(jù)從磁盤讀入;從磁盤讀入數(shù)據(jù)到內(nèi)存中,會影響正常業(yè)務的 Buffer Pool 命中率,而且這個算法天然會對被驅(qū)動表的數(shù)據(jù)做多次訪問,更容易將這些數(shù)據(jù)頁放到 Buffer Pool 的頭部。

  2. 即使被驅(qū)動表數(shù)據(jù)都在內(nèi)存中,每次查找“下一個記錄的操作”,都是類似指針操作。而 join_buffer 中是數(shù)組,遍歷的成本更低。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,048評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,414評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,169評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,722評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,465評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,823評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,000評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,554評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,295評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,513評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,722評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,125評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,430評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,237評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,482評論 2 379

推薦閱讀更多精彩內(nèi)容

  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當在唯一索引所對應的列上鍵入重復值時,會觸發(fā)此異常。 O...
    我想起個好名字閱讀 5,399評論 0 9
  • 背景: 閱讀新聞 12C CDB模式下RMAN備份與恢復 [日期:2016-11-29] 來源:Linux社區(qū) 作...
    陽屯okyepd閱讀 3,553評論 0 7
  • 今天看到一位朋友寫的mysql筆記總結,覺得寫的很詳細很用心,這里轉(zhuǎn)載一下,供大家參考下,也希望大家能關注他原文地...
    信仰與初衷閱讀 4,749評論 0 30
  • 什么是數(shù)據(jù)庫? 數(shù)據(jù)庫是存儲數(shù)據(jù)的集合的單獨的應用程序。每個數(shù)據(jù)庫具有一個或多個不同的API,用于創(chuàng)建,訪問,管理...
    chen_000閱讀 4,051評論 0 19
  • 今天自己開車帶孩子們回了家,認真的把車盡量停好,好像還不是十分的規(guī)矩,這就樣吧!反正停車位比較多。...
    悠然_3c09閱讀 187評論 0 3