一張圖搞懂 SQL JOIN
2025年11月12日
SQL JOIN 可以用來連接兩個或多個表,以便在一次查詢中取得來自多個表的資料。在實際開發中,我們經常需要從多張表中組合出所需資料,而 JOIN 正是完成這項任務的核心工具。
從本質來看,SQL JOIN 對兩個資料列集合,進行集合運算,接受兩個集合,再產生一個新的集合。JOIN 會根據兩個表格中的關聯欄位,以及 JOIN 的方式,來取得對應的資料。舉例來說,假如有一張 users 表和一張 orders 表,如果要查詢「每個使用者及該使用者的訂單」,就可以透過 JOIN 來做到。
在眾多學習 SQL JOIN 的初學教材中,最推薦 Visual JOIN 這個小工具,用視覺化的方式帶讀者理解不同類型的 JOIN,分別可以拿到哪些資料。
除此之外,在社群中有流傳一張 SQL JOIN 大抄,一次彙整各類常見的 JOIN 方法,讓我們一起來看看。為了讓讀者們更容易理解,以下我們用 users 與 orders 這兩張表為例。
首先是 users 這張表,有 user_id、name,以及 email:
user_id | name | email
--------|-----------|------------------
1 | 王小明 | ming@email.com
2 | 李小美 | mei@email.com
3 | 張大華 | hua@email.com
4 | 陳小新 | new@email.com
接著是 orders 這張表,有 order_id、user_id、amount、date 等欄位:
order_id | user_id | amount | date
---------|---------|--------|------------
101 | 1 | 1500 | 2025-01-15
102 | 1 | 2300 | 2025-02-20
103 | 2 | 800 | 2025-03-10
104 | NULL | 500 | 2025-03-15 (訪客結帳)
拿到所有資料的 FULL JOIN

假如我們想要拿到兩張表的所有資料,可以透過 FULL JOIN,這樣即使沒有匹配,也會保留兩張表的所有內容。
-- 概念來說 --
SELECT *
FROM A
FULL JOIN B ON A.key = B.key;
-- 以使用者與訂單為例 --
SELECT *
FROM users
FULL JOIN orders ON users.user_id = orders.user_id
-- 會拿到 --
user_id | name | order_id | user_id | amount
--------|-------------|----------|---------|-------
1 | 王小明 | 101 | 1 | 1500
1 | 王小明 | 102 | 1 | 2300
2 | 李美麗 | 103 | 2 | 800
3 | 張大華 | NULL | NULL | NULL
4 | 陳小新 | NULL | NULL | NULL
NULL | NULL | 104 | NULL | 500
可以看到,即使張大華與陳小新兩個人,都沒有下單任何東西,仍然會出現在表上。同時,即使某位訪客下單了編號 104 的訂單,因為是訪客沒有使用者 id,但因為是用 FULL JOIN,所以仍會出現在結果中。
只拿到匹配資料的 INNER JOIN

接著,如果想要拿到兩張表有匹配的資料,可以用 INNER JOIN。所謂的匹配是指兩邊都有,假如一個使用者沒有下單任何東西,把兩張表用 INNER JOIN,就不會出現該使用者。假如某個訂單沒有對應的使用者 (例如訪客訂單),也不會出現。
-- 概念來說 --
SELECT *
FROM A
INNER JOIN B ON A.key = B.key;
-- 以使用者與訂單為例 --
SELECT *
FROM users
INNER JOIN orders ON users.user_id = orders.user_id
-- 會拿到 --
user_id | name | order_id | user_id | amount
--------|---------|----------|---------|-------
1 | 王小明 | 101 | 1 | 1500
1 | 王小明 | 102 | 1 | 2300
2 | 李美麗 | 103 | 2 | 800
可以看到在 INNER JOIN 下,張大華跟陳小新沒出現,因為他們沒有任何訂單;同時訂單編號 104 的訂單也沒出現,因為是訪客帳號沒有使用者 id。
拿兩邊沒交集的資料

如果想要反過來,不是拿兩邊有匹配的資料,而是只要拿兩邊沒有匹配的資料,則可以透過以下的方式做到。
-- 概念來說 --
SELECT *
FROM A
FULL JOIN B ON A.key = B.key
WHERE A.key IS NULL OR B.key IS NULL;
-- 以使用者與訂單為例 --
SELECT *
FROM users
FULL JOIN orders ON users.user_id = orders.user_id
WHERE users.user_id IS NULL OR orders.user_id IS NULL
-- 會拿到 --
user_id | name | order_id | user_id | amount
--------|-------------|----------|---------|-------
3 | 張大華 | NULL | NULL | NULL
4 | 陳小新 | NULL | NULL | NULL
NULL | NULL | 104 | NULL | 500
最常用的 LEFT JOIN

在實務工作中,經常會要找不對稱的資料,例如「從沒有下過單的使用者」,在這種狀況下,就需要用上 LEFT JOIN。
具體來說,LEFT JOIN 做的事情是拿到 A 表中所有的資料,然後 B 表有交集的就順便帶上。以這邊的例子來說,用 LEFT JOIN 就代表「拿所有使用者的資料,如果有訂單的話,就一併拿,沒有的話也沒關係」。
-- 概念來說 --
SELECT *
FROM A
LEFT JOIN B ON A.key = B.key;
-- 以使用者與訂單為例 --
SELECT *
FROM users
LEFT JOIN orders ON users.user_id = orders.user_id
-- 會拿到 --
user_id | name | order_id | user_id | amount
--------|-------------|----------|---------|-------
1 | 王小明 | 101 | 1 | 1500
1 | 王小明 | 102 | 1 | 2300
2 | 李美麗 | 103 | 2 | 800
3 | 張大華 | NULL | NULL | NULL
4 | 陳小新 | NULL | NULL | NULL
但只單靠 LEFT JOIN 沒辦法拿到「從沒有下過單的使用者」,我們需要進一步做篩選。這時可以搭配 WHERE B.key IS NULL;,把沒有下單的使用者篩出來。

-- 概念來說 --
SELECT *
FROM A
LEFT JOIN B ON A.key = B.key
WHERE B.key IS NULL;
-- 以使用者與訂單為例 --
SELECT *
FROM users
LEFT JOIN orders ON users.user_id = orders.user_id
WHERE orders.order_id IS NULL
-- 會拿到 --
user_id | name | order_id | user_id | amount
--------|-------------|----------|---------|-------
3 | 張大華 | NULL | NULL | NULL
4 | 陳小新 | NULL | NULL | NULL
這個概念可以延伸到很多地方,例如要找「哪些商品沒有庫存」、「哪些會員沒登入過」、「哪些廣告沒被點擊過」,都可以用同樣的邏輯來找。從產品面來說,就可以去找出「註冊超過 30 天但從未購買的使用者」,發送優惠券刺激購買。
拿到右表所有內容的 RIGHT JOIN

最後讓我們來看與 LEFT JOIN 相對的 RIGHT JOIN ,透過 RIGHT JOIN,我們可以拿到 B 表的所有資料,如果 A 表有的話就順便拿。
以使用者與訂單的例子來說,會拿到所有訂單,即使沒有使用者也一樣會拿。所以在下面的例子中,會看到張大華與陳小新沒有出現,因為這兩位使用者沒有訂單。
-- 概念來說 --
SELECT *
FROM A
RIGHT JOIN B ON A.key = B.key;
-- 以使用者與訂單為例 --
SELECT *
FROM users
RIGHT JOIN orders ON users.user_id = orders.user_id
-- 會拿到 --
user_id | name | order_id | user_id | amount
--------|-------------|----------|---------|-------
1 | 王小明 | 101 | 1 | 1500
1 | 王小明 | 102 | 1 | 2300
2 | 李美麗 | 103 | 2 | 800
NULL | NULL | 104 | NULL | 500

同樣地,如果想要進一步拿沒有使用者的訂單 (例如訪客訂單),可以搭配 WHERE 語句來做到。在下方的例子可以看到,訂單編號 104 的訂單沒有使用者。
-- 概念來說 --
SELECT *
FROM A
RIGHT JOIN B ON A.key = B.key
WHERE A.key IS NULL;
-- 以使用者與訂單為例 --
SELECT *
FROM users
RIGHT JOIN orders ON users.user_id = orders.user_id
WHERE users.user_id IS NULL
user_id | name | order_id | user_id | amount
--------|------|----------|---------|-------
NULL | NULL | 104 | NULL | 500
在實務上,雖然可以用 RIGHT JOIN,但多數開發者會以 LEFT JOIN 為主,因為思考起來比較直觀。當讀到 FROM users RIGHT JOIN orders 時,因為是從左讀到右,因此自然會先看到 users,但實際上 orders 才是 RIGHT JOIN 的主角,所以這讓人感到有點違反直覺。
閱讀更多
如果你想成為技術能力更扎實的後端工程師,歡迎加入 E+ 成長計畫;透過深度內容與線上課程,打好技術基礎,在職涯中持續成長。