iT邦幫忙

2021 iThome 鐵人賽

DAY 19
1
Modern Web

網站一條龍 - 從架站到前端系列 第 19

[Day19] MySQL 的 JOIN

上次我們介紹了 MySQL 的簡單 CRUD,雖然本系列本來只打算介紹基礎中的基礎,然後再開傳送們給有興趣的邦友延伸閱讀,但是後來想一想,介紹 MySQL 沒講 JOIN 跟 Stored Procedure (SP)總覺得怪怪的,所以筆者最後決定資料庫多寫兩天,簡單的介紹一下 JOIN 跟 SP。今天我們就來介紹一下資料庫超級常用的 JOIN。

MySQL 的 JOIN

一般而言,我們會盡量避免讓同樣的資料散落在不同的資料表。我們會把這些共同會用到的資料「抽出來」放在另一張表,然後透過一個共通的欄位做關聯,從兩個資料表把資料「串」起來,而這個「串」的動作就是 JOIN。

MySQL 的 JOIN 有三種:INNER JOIN, LEFT OUTER JOIN與 RIGHT OUTER JOIN。假設我們用一個共通欄位 c 來做關聯

  • INNER JOIN 只會取得兩個資料表中 c 有匹配上的資料列
  • LEFT OUTER JOIN 會取得所有左邊資料表所有的資料列,沒有匹配上的部份就補 NULL
  • RIGHT OUTER JOIN 與 LEFTOUTER JOIN 相反,會取得所有右邊資料表的資料列,沒有匹配上的部份就補 NULL
    https://ithelp.ithome.com.tw/upload/images/20210919/2014066409HhnhkvWy.png

INNER 與 OUTER 兩個字通常會被省略,變成 JOIN, LEFT JOIN, RIGHT JOIN

另外有一種 JOIN 方式叫做 FULL JOIN,只要兩個資料表任何一邊有資料就讀出,但是使用的機會少,MySQL 也沒有內建支援這個語法,所以本篇就不介紹。

JOIN 範例

現在我們新增了一張表:user_log,用來儲存每個使用者的行為紀錄。user_log 與 user 兩個資料表的共通欄位是 user_id,以下我們將用這個欄位來做 JOIN 示範。

https://ithelp.ithome.com.tw/upload/images/20210919/20140664XL51gW3jLJ.png

user_log 中的 action 其實也應該抽出來放在另一張表,但是為了方便解釋語法,這邊先讓這些資料直接寫在這。

  • INNER JOIN
SELECT U.user_id, U.user_name, L.action, L.action_datetime
FROM user AS U INNER JOIN user_log AS L
ON U.user_id = L.user_id

上面的 SQL 腳本以共通的欄位 user_id 對 user 與 user_log 兩張資料表做關聯,從 user 資料表取出 user_id 與 user_name 兩個欄位、從 user_log 取出 action 與 action_datetime 欄位。AS 關鍵字可以替來源資料表取別名,上面的腳本中,U 是 user 資料表的別名,L 是 user_log 資料表的別名。ON 關鍵字用來做關聯的判斷,在上面的例子中,關聯成立的條件是 user 與 user_log 兩張表中的 user_id 必須相等。

從 JOIN 的結果我們可以看到,我們把使用者資料與 log 關聯起來了。但是INNERR JOIN 不會取得 user_id = 3 的資料與 log_id = 5 的資料,因為這些資料的 user_id 無法在另一張表中被匹配到。
https://ithelp.ithome.com.tw/upload/images/20210919/2014066457S3TWrES4.png

  • LEFT JOIN
SELECT U.user_id, U.user_name, L.action, L.action_datetime
FROM user AS U LEFT OUTER JOIN user_log AS L
ON U.user_id = L.user_id

上面的語法一樣以 user_id 欄位做關聯取出使用者名稱與 log 紀錄,唯一的差別是,沒有 log 紀錄的 Cathy 也出現在結果中了,因為 Cathy 是左邊的來源資料表,即使他沒有匹配到任何一筆 log 他仍然被讀取出來。
https://ithelp.ithome.com.tw/upload/images/20210919/20140664FWOSexw6af.png

  • RIGHT JOIN
SELECT U.user_id, U.user_name, L.action, L.action_datetime
FROM user AS U RIGHT OUTER JOIN user_log AS L
ON U.user_id = L.user_id

上面的語法一樣以 user_id 欄位做關聯取出使用者名稱與 log 紀錄,唯一的差別是,bug 觸發的 log 也出現在查詢結果中了,即使他的 user_id 是 -1 沒有匹配到任何使用者。而且我們的指令中選擇從 user 資料表取出 user_id,所以這邊這筆 log 的 user_id 變成了 NULL
https://ithelp.ithome.com.tw/upload/images/20210919/20140664iHbHJOWX8s.png

語法慣例

上面的語法雖然都可以運作,但卻不是實務上最常寫的方式,實務上的慣例為

  1. INNER 通常會省略,只寫 SELECT ... FROM t1 JOIN t2 ON ...。甚至可以連 JOIN 都省略,只用逗號隔開兩個資料表,然後用 WHERE 取代 ON,寫成 SELECT ... FROM t1, t2 WHERE ...

  2. OUTER 通常會省略,寫成 LEFT JOIN 或 RIGHT JOIN

  3. 大多會用 LEFT JOIN。只要把資料表的順序對調,RIGHT JOIN 就會變成 LEFT JOIN,實務上比較少會用 RIGHT JOIN,大多會把順序倒過來用 LEFT JOIN

明天,我們將繼續來介紹 Stored Procedure,介紹完就來幫我們的 .NET API 連上資料庫。


上一篇
[Day18] MySQL 的 CRUD 語法
下一篇
[Day20] MySQL 的 Stored Procedure
系列文
網站一條龍 - 從架站到前端33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言