MySQL 中的 JOIN 查詢語句總結

JOIN 關鍵字在多表查詢中被廣泛使用,本文圍繞笛卡爾積,7 種連接方式,以及 3 種連接條件進行分析,並通過一個簡單的案例進行演示。

1. 連接方式和連接條件

7 種不同的連接方式可以總結爲下面的韋恩圖,下文會介紹實現這 7 種連接方式的 SQL 語句。

在這裏插入圖片描述3 種連接條件對應的關鍵字分別爲 NATURAL,USING,ON。總的來說,這三個關鍵字的表達能力從前往後逐漸增強,也就是後面的關鍵字能實現前面的關鍵字所能實現的功能。

這 7 種連接方式和 3 種連接條件之間可以互相組合,所以可以組合出 21 種不同的連接語句,但是隻要理解了 3 種連接條件之間的不同,可以很容易地分析出,不同連接方式下,這 3 種連接條件的查詢結果。所以下文僅在內連接部分對比 3 種連接條件,其餘的連接方式均使用 ON 關鍵字實現。

2. 建表

我們用一個英雄(hero)表,和一個武器(weapon)表,來演示各種連接方式和連接條件。

hero 表有 3 個字段:英雄名(name),英雄的重量(weight)和使用的武器名(weapon_name)。weapon 表有 2 個字段:武器名(weapon_name)和武器的重量(weight)。

以下是 hero 表的建表語句,插入語句,查詢語句和查詢結果。

CREATE TABLE hero (
	name VARCHAR(20),
	weight int,
	weapon_name VARCHAR(30)
) DEFAULT CHARSET UTF8;
INSERT INTO hero VALUES	('張飛', 100, '丈八蛇矛'),
						('關羽', 90, '青龍偃月刀'),
						('劉備', 80, '雌雄雙股劍'),
						('諸葛亮', 70, '諸葛連弩');						
SELECT * FROM hero;

在這裏插入圖片描述
以下是 weapon 表的建表語句,插入語句,查詢語句和查詢結果。

CREATE TABLE weapon (
	weapon_name VARCHAR(30),
	weight INT	
) DEFAULT CHARSET UTF8;
INSERT INTO weapon VALUES	('青龍偃月刀', 40),
							('丈八蛇矛', 30),
							('雌雄雙股劍', 20),
							('方天畫戟', 30);		
SELECT * FROM weapon;

在這裏插入圖片描述

這兩張表就創建好了,下面我們逐個分析各種連接方式。

3. 笛卡爾積

笛卡爾積可以理解爲 for 循環裏嵌套了另一個 for 循環。hero 表和 weapon 表裏都有 4 條記錄,所以用直接使用笛卡爾積查詢後的結果應該有 16 條記錄。

查詢語句和查詢結果如下。

SELECT * FROM hero, weapon;


如果我們想在 hero 表的基礎上插入一列,該列顯示武器的重量,我們可以在笛卡爾積的基礎上,加一條 WHERE 語句:

SELECT * FROM hero AS H, weapon AS W
WHERE H.weapon_name = W.weapon_name;

在這裏插入圖片描述
WHERE 語句的功能可以視爲,對笛卡爾積的查詢結果進行篩選,篩選條件是 H.weapon_name = W.weapon_name。

這裏有兩點需要注意:1) 查詢結果中沒有 ‘諸葛亮’ 。2)查詢結果中出現了兩列 weapon_name 完全一樣。

第一點是因爲 ‘諸葛亮’ 使用的武器 ‘諸葛連弩’,沒有出現在 weapon 表裏面,所以 WHERE 語句把笛卡爾積查詢結果裏的所有包含 ‘諸葛亮’ 記錄都給刪除掉了。如果我們想在查詢結果中保留 ‘諸葛亮’ 這條記錄的話,就需要用到左外連接

第二點是使用 WHERE 語句和使用 ON 關鍵字的相同之處,也是他們兩個和 NATURAL 、USING 關鍵字不一樣的地方。前兩者的查詢結果中都會出現這樣名稱相同的兩列(注意這裏說的是名稱相同,使用 ON 關鍵字的查詢結果中這兩個列的名稱雖然一樣,但是內容可能會有不同,下文會說明),後兩者只保存一列。

4. 內連接

內連接(INNER JOIN)查詢,顧名思義,只會保留 hero 表和 weapon 表中公有的記錄。

在這裏插入圖片描述

ON 關鍵字後面指定的查詢條件與上文的 WHERE 語句一樣,但是二者的作用範圍是有所不同的,在左外連接部分會做出解釋。

下是使用 ON 關鍵字的內連接語句,及其查詢結果。

SELECT *
FROM hero INNER JOIN weapon ON hero.weapon_name = weapon.weapon_name;

(其中的 INNER 關鍵字可省略。)
在這裏插入圖片描述

可以看到,這裏的查詢結果與使用 笛卡爾積 + WHERE 語句 的查詢結果是一樣的。

這裏如果用 USING 語句代替 ON 語句的話可以寫爲:

SELECT *
FROM hero INNER JOIN weapon USING (weapon_name);

在這裏插入圖片描述
可以看到,這裏的查詢結果與使用 ON 關鍵字的查詢結果基本一致,只不過只保留了一列 weapon_name。

ON 的表達能力比 USING 強,是因爲 ON 後面可以是兩個表中列名不一樣的列,比如 hero.name = weapon.weapon_name,當然這個查詢會返回空集,但是這條語句是沒有問題的。但是 USING 後面只能是兩個表中列名一樣的列。注意到兩個表中 weapon_name 和 weight 列都是重名的,USING 就可以指定兩個中的一個,或者兩個都指定,作爲連接條件。

而 NATURAL 關鍵字會自動識別兩個表中列名一樣的列,然後將這些列的值都一樣作爲連接條件。

NATURAL 語句及其查詢結果爲:

SELECT * 
FROM hero NATURAL JOIN weapon;

在這裏插入圖片描述
返回空集是因爲兩個表中沒有 weight 和 weapon_name 都一樣的記錄。

如果用 USING 和 ON 關鍵字替換 NATURAL 可以寫成:

SELECT * 
FROM hero JOIN weapon USING(weight, weapon_name);

SELECT * 
FROM hero JOIN weapon ON hero.weight = weapon.weight AND hero.weapon_name = weapon.weapon_name;

三者實現的功能是等價的,區別只是使用 ON 會保留所有列。

如果把劉皇叔的體重改成 20 的話,三個查詢語句的查詢結果如下。

UPDATE hero SET weight = 20 WHERE name = '劉備';

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
以上就是內連接查詢的實現方式,以及 3 種查詢條件之間的對比。

5. 三種外連接

左外連接右外連接和全外連接的韋恩圖如下。相對於內連接來講,左外連接保留了 JOIN 關鍵字左邊的表中的所有記錄,右外連接則保留了 JOIN 右邊表中的所有記錄,全外連接保留了兩個表的所有記錄。
在這裏插入圖片描述左外連接的 SQL 語句和查詢結果如下。

SELECT * 
FROM hero LEFT OUTER JOIN weapon ON hero.weapon_name = weapon.weapon_name;

(OUTER 關鍵字可以省略)
在這裏插入圖片描述

這裏我們就可以對比一下左外連接中的 ON 和笛卡爾積查詢中的 WHERE 的區別了。ON 指定的是連接條件,它的作用僅限於參與連接的那些記錄,也就是出現在內連接結果集中的那些記錄,所以即使 ‘諸葛亮’ 那條記錄不滿足 ON 後面的條件,這條記錄也會出現在結果集中。而使用 WHERE 的查詢語句,結果集中的所有記錄,都必須滿足 WHERE 後面的條件。

右外連接的 SQL 語句和查詢結果如下。

SELECT * 
FROM hero RIGHT JOIN weapon ON hero.weapon_name = weapon.weapon_name;

在這裏插入圖片描述
全外連接的 SQL 語句和查詢結果如下。

SELECT * 
FROM hero FULL JOIN weapon ON hero.weapon_name = weapon.weapon_name;

在這裏插入圖片描述
結果是沒有結果。。

我也不太清楚爲什麼會有這樣一個錯誤。但是條條大路通羅馬,我們可以把左外連接和右外連接的結果進行 UNION 操作,來得到全外連接。

(SELECT * 
FROM hero RIGHT JOIN weapon ON hero.weapon_name = weapon.weapon_name)
UNION
(SELECT * 
FROM hero LEFT JOIN weapon ON hero.weapon_name = weapon.weapon_name);

在這裏插入圖片描述

6. 三種叫不出名字的連接

三種連接方式如下圖所示,第一種的意思是找出 hero 表中 weapon_name 沒在 weapon 表中出現過的記錄,第二種和第三種應該也很好理解,但是表達成文字就很繞了。這三種情況可以在左外連接,右外連接,或者全外連接的基礎上,加一條 WHERE 語句實現。
在這裏插入圖片描述第一種連接的 SQL 語句和查詢結果如下:

SELECT * 
FROM hero LEFT JOIN weapon ON hero.weapon_name = weapon.weapon_name
WHERE weapon.weapon_name IS NULL;

在這裏插入圖片描述
如果這條語句作爲一個整體無法執行的話,試試這樣子輸入它們。

在這裏插入圖片描述
第二種如下。

SELECT *
FROM hero RIGHT JOIN weapon ON hero.weapon_name = weapon.weapon_name
WHERE hero.weapon_name IS NULL;

在這裏插入圖片描述
第二種連接的實現方式同理。

第三種因爲全外連接無法實現,下面這條語句想必也無法執行,如果哪位讀者知道爲什麼,歡迎留言,我用的是 MySQL 8.0 版本。

SELECT *
FROM hero FULL JOIN weapon ON hero.weapon_name = weapon.weapon_name
WHERE hero.weapon_name IS NULL OR weapon.weapon_name IS NULL;

在這裏插入圖片描述
前兩種連接也可以用子查詢實現。以第一種連接爲例:

SELECT * 
FROM hero 
WHERE weapon_name NOT IN (SELECT weapon_name FROM weapon);

在這裏插入圖片描述
總結:JOIN 關鍵字在多表查詢中被廣泛使用,本文通過一個簡單的案例,演示了笛卡爾積、7 種連接法方式和 3 種查詢條件,其中 2 種連接方式無法執行,可以改用 UNION 實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章