一張圖搞懂 SQL JOIN

2025年11月12日

💎 加入 E+ 成長計畫 與超過 800+ 位工程師一同在社群成長,並獲得更多深度的軟體前後端學習資源

SQL JOIN 可以用來連接兩個或多個表,以便在一次查詢中取得來自多個表的資料。在實際開發中,我們經常需要從多張表中組合出所需資料,而 JOIN 正是完成這項任務的核心工具。

從本質來看,SQL JOIN 對兩個資料列集合,進行集合運算,接受兩個集合,再產生一個新的集合。JOIN 會根據兩個表格中的關聯欄位,以及 JOIN 的方式,來取得對應的資料。舉例來說,假如有一張 users 表和一張 orders 表,如果要查詢「每個使用者及該使用者的訂單」,就可以透過 JOIN 來做到。

在眾多學習 SQL JOIN 的初學教材中,最推薦 Visual JOIN 這個小工具,用視覺化的方式帶讀者理解不同類型的 JOIN,分別可以拿到哪些資料。

除此之外,在社群中有流傳一張 SQL JOIN 大抄,一次彙整各類常見的 JOIN 方法,讓我們一起來看看。為了讓讀者們更容易理解,以下我們用 usersorders 這兩張表為例。

首先是 users 這張表,有 user_idname,以及 email

user_id | name      | email
--------|-----------|------------------
1       | 王小明     | ming@email.com
2       | 李小美     | mei@email.com
3       | 張大華     | hua@email.com
4       | 陳小新     | new@email.com

接著是 orders 這張表,有 order_iduser_idamountdate 等欄位:

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
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

接著,如果想要拿到兩張表有匹配的資料,可以用 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

拿兩邊沒交集的資料

image

如果想要反過來,不是拿兩邊有匹配的資料,而是只要拿兩邊沒有匹配的資料,則可以透過以下的方式做到。

-- 概念來說 --
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

image

在實務工作中,經常會要找不對稱的資料,例如「從沒有下過單的使用者」,在這種狀況下,就需要用上 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;,把沒有下單的使用者篩出來。

image
-- 概念來說 --
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

image

最後讓我們來看與 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
image

同樣地,如果想要進一步拿沒有使用者的訂單 (例如訪客訂單),可以搭配 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+ 成長計畫;透過深度內容與線上課程,打好技術基礎,在職涯中持續成長。

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們