[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 连上资料库。


<<:  OpenStack Neutron 介绍 — OVN vs OVS

>>:  【Day 6】机器学习基本功(四)

【基础影像应用篇】DAY6.模型训练流程

恭喜你完成物件标记! 还不知道如何操作物件标记或忘记物件标记流程的赶快点开DAY4专案建立与影像标记...

第二十七章、燃烧吧!Three.js 小宇宙!(伍)

前言 嗨大家好,今天是铁人赛的第 27 天,也是本系列 Three.js 最後一篇罗,照惯例,最後一...

iOS APP 开发 OC 第二十三天,Foundation 的 NSString

tags: OC 30 day 近来到台北工作,好久没有吃到好吃的卤肉饭了。 在看Foundatio...

Day 20 -SQL 函数 COUNT()!

COUNT() 函数用来计算符合查询条件的栏位纪录总共有几笔。 若栏位值为 NULL,则该笔记录不会...

作业系统 Critical section

记录学习内容。 以下内容大多引用大大们的文章,加上一些自己的笔记。 自己的笔记部分,内容可能有错误。...