由於工作需要,現在也開始寫一些複雜一點的 sql 了。由於之前對於 join
與 where
關鍵字的使用一直有疑惑,故寫一篇文章整理一下,算是對 sql 中各種表聯結使用的一個總結。
SQL JOIN 的作用
sql 關鍵字 union
與 intersect
屬於以行方向爲單位的集合運算,進行這些集合運算時,會導致記錄行數的增加或減少。
關鍵字 join
則是屬於列方向的運算,將其他表中的列添加過來。我們稱這種操作爲聯結。
sql 聯結根據其用途可以分爲多種,這裏主要講述內聯結與外聯結。
INNER JOIN
內聯結,inner join,是使用最廣泛的聯結運算。我們以例子來說明內聯結的含義。
student
表結構與記錄以下:
student_id | student_name |
---|---|
1 | Leo |
2 | Lee |
3 | Hao |
score
表結構與記錄以下:
student_id | student_score |
---|---|
1 | 89 |
2 | 95 |
可以看到 student_id
列存在於 student
表和 score
表,其他列則只存在於一張表。可以將 student_id
列作爲橋樑,將兩表滿足同樣條件的列彙集到同一結果之中,這就是聯結運算。
可以看到,學生 Leo 的 id 爲 1,分數在 score
表,Leo 的分數爲 89 分。學生 Lee 的 id 爲 2,分數爲 95。學生 Hao 的 id 爲 3,但 score
表中沒有 Hao 的分數。
使用 inner join
對 student
表和 score
表進行聯結,sql 以下所示:
select st.student_id, st.student_name, sc.student_score
from student st
inner join score sc on st.student_id = sc.student_id;
執行 sql 結果如下所示。
student_id | student_name | student_score |
---|---|---|
1 | Leo | 89 |
2 | Lee | 95 |
關於 inner join
的使用,有幾點需要注意的。
- from 子句
from
子句使用 inner join
,將兩張表聯結起來。
- on 子句
on
子句是聯結的條件,例如我們使用 student_id
作爲聯結條件:
on st.student_id = sc.student_id
- select 子句
select
子句用於指定提取哪些列。
- 與 where 子句結合
如果我們只想看查 student_id
爲 1 的情況,可以繼續使用 where
子句。
select st.student_id, st.student_name, sc.student_score
from student st
inner join score sc on st.student_id = SC.student_id
where st.student_id = 1;
執行結果以下:
student_id | student_name | student_score |
---|---|---|
1 | Leo | 89 |
OUTER JOIN
外聯結,outer join,與內聯結一樣,也是通過 on
子句將兩張表聯結起來,只是結果有所不同。
爲了說明外聯結與內聯結的不同,我們將上面內聯結的例子轉換成外聯結,看下執行結果。
select st.student_id, st.student_name, sc.student_score
from student st
left outer join score sc on st.student_id = sc.student_id;
執行結果如下表所示:
student_id | student_name | student_score |
---|---|---|
1 | Leo | 89 |
2 | Lee | 95 |
3 | Hao |
- 外聯結選取出單張表全部記錄
與內聯結相比,外聯結的不同點在於結果的行數不一樣。內聯結執行結果有 2 條記錄,外聯結卻有 3 條記錄,增加的 1 條記錄是 student_id
爲 3 的學生 Hao。雖然 score
表並沒 student_id
爲 3 的成績,但執行結果仍然把這條記錄提取出來了。在實際應用中,如果我們想生成固定行數的數據時,就需要使用外聯結。
內聯結只會提取同時存在於兩張表中的數據,因此 Hao 學生記錄並不會出現。對於外聯結來說,只要數據存在於某一張表中,就能夠提取出來。
同時我們也看到,由於 score
表中並沒有 Hao 學生的成績,所以執行結果該字段爲 null。
- 外聯結可以指定主表
外聯結可以指定哪張表爲主表,查詢結果會包含主表的所有記錄。使用 left
時 from 子句中寫在左側的表是主表,使用 right
時右側的表是主表。上述外聯結代碼使用 left
,因此左側的 student
表是主表。
我們也可以使用 right
對代碼進行改寫,效果完全一樣。
select st.student_id, st.student_name, sc.student_score
from score sc
right outer join student st on st.student_id = sc.student_id;
至於使用 left
或者 right
,使用哪一個都可以,通常來說,使用 left
的情況多一些。
CROSS JOIN
交叉聯結,cross join,是指對聯結的表進行笛卡爾積運算。
我們對 student
表和 score
表進行交叉聯結:
select st.student_id, st.student_name, sc.student_id, sc.student_score
from student st
cross join score sc;
查詢結果如下:
student_id | student_name | student_id | student_score |
---|---|---|---|
1 | Leo | 1 | 89 |
2 | Lee | 1 | 89 |
3 | Hao | 1 | 89 |
1 | Leo | 2 | 95 |
2 | Lee | 2 | 95 |
3 | Hao | 2 | 95 |
進行交叉聯結時,無法使用內聯結和外聯結中所使用的 on 子句,這是因爲交叉聯結是對兩張表中的全部記錄進行交叉組合,結果記錄數是兩張表中行數的乘積。student
表包含 3 條記錄,score
表包含 2 條記錄,故交叉聯結結果包含 3 * 2 = 6 條記錄。
內聯結是交叉聯結的一部分,“內”可以理解爲“包含在交叉聯結結果中的部分”。相反,外聯結的“外”可以理解爲“交叉聯結結果之外的部分”。
在實際應用中,交叉聯結使用較少。一是其結果沒有實用價值,二是由於結果太多,需要花費大量的運算時間。
過時的語法
上述 sql 代碼中,我們使用 inner join
,outer join
等聯結關鍵字符合 sql 標準,但實際開發中,卻經常會碰到使用 where
關鍵字來實現聯結。
將上面我們實現內聯結的 sql 代碼替換成使用 where
來實現:
select st.student_id, st.student_name, sc.student_score
from student st, score sc
where st.student_id = sc.student_id and st.student_id = 1;
執行結果以下:
student_id | student_name | student_score |
---|---|---|
1 | Leo | 89 |
這樣的書寫方式的查詢結果與標準語法的查詢結果相同,但這種語法不僅已過時,還存在其他問題:
- 難以判斷是內聯結還是外聯結
- 難以判斷哪部分是聯結條件,哪部分是用來選取記錄的限制條件
- 可能在將來不能使用
雖然不建議使用過時的語法,但我們仍然需要具備閱讀這些過時語法代碼的能力 ?
參考資料
- SQL 基礎教程,第 2 版,MICK 著,孫淼,羅勇譯