想像一下,你有兩個表格:一個是學生清單,一個是他們選課的紀錄。不是每個學生都有選課,你想看到所有學生的完整名單,包括那些還沒選課的。用 INNER JOIN 你只會看到有選課的學生,那其他人怎麼辦?這時就要用 LEFT JOIN 啦。
LEFT JOIN 會回傳 左表(查詢裡第一個表)所有的資料列,還有 右表裡對應的資料列。如果沒對應,右表的欄位就會是 NULL。
LEFT JOIN 的語法:
SELECT
表1.欄位1,
表1.欄位2,
表2.欄位1,
表2.欄位2
FROM
表1 LEFT JOIN 表2
ON
表1.共同欄位 = 表2.共同欄位;
表1— 就是「左表」。表2— 就是「右表」。共同欄位— 兩個表要 JOIN 的那個欄位。
簡單例子
如果 students 表長這樣:
| student_id | name |
|---|---|
| 1 | Otto |
| 2 | Anna |
| 3 | Peter |
然後 enrollments 表長這樣:
| enrollment_id | student_id | course |
|---|---|---|
| 1 | 1 | 數學 |
| 2 | 1 | 物理 |
| 3 | 2 | 生物 |
那這個查詢:
SELECT
students.name,
enrollments.course
FROM
students LEFT JOIN enrollments
ON
students.student_id = enrollments.student_id;
會得到:
| name | course |
|---|---|
| Otto | 數學 |
| Otto | 物理 |
| Anna | 生物 |
| Peter | NULL |
你看,所有學生都在結果裡,連 Peter 這個還沒選課的也有。對 Peter 來說,course 欄位就是 NULL。
LEFT JOIN 的使用範例
範例 1:拿到所有學生和他們的課程
假設我們要拿到學生的完整名單,還有他們選了哪些課。如果學生還沒選課,也要顯示出來。
查詢一樣:
SELECT
students.name,
enrollments.course
FROM
students LEFT JOIN enrollments
ON
students.student_id = enrollments.student_id;
結果:
| name | course |
|---|---|
| Otto | 數學 |
| Otto | 物理 |
| Anna | 生物 |
| Peter | NULL |
這就是 LEFT JOIN 的經典用法啦。
範例 2:列出商品和它們的銷售量
假設你有兩個表:
products 表,放所有商品:
| product_id | product_name |
|---|---|
| 1 | 智慧型手機 |
| 2 | 平板 |
| 3 | 筆記型電腦 |
sales 表,放銷售資料:
| sale_id | product_id | quantity |
|---|---|---|
| 1 | 1 | 5 |
| 2 | 1 | 3 |
| 3 | 2 | 2 |
現在你想看到所有商品和它們的銷售量,連還沒賣出去的也要顯示。
SELECT
products.product_name,
SUM(sales.quantity) AS total_sold
FROM
products LEFT JOIN sales
ON
products.product_id = sales.product_id
GROUP BY
products.product_name;
結果:
| product_name | total_sold |
|---|---|
| 智慧型手機 | 8 |
| 平板 | 2 |
| 筆記型電腦 | NULL |
LEFT JOIN 的注意事項和小問題
一定要 NULL 嗎?
有時候 LEFT JOIN 會讓你看到一些意外的 NULL。這時可以用 COALESCE() 把 NULL 換成你想要的字串。
SELECT
students.name,
COALESCE(enrollments.course, '尚未選課') AS course
FROM
students LEFT JOIN enrollments
ON
students.student_id = enrollments.student_id;
結果:
| name | course |
|---|---|
| Otto | 數學 |
| Otto | 物理 |
| Anna | 生物 |
| Peter | 尚未選課 |
重複資料太多
如果右表有重複的資料,查詢結果會比你想像的還多行。記得檢查資料,有需要可以加 DISTINCT 避免重複。
GO TO FULL VERSION