為了便於理解,本文將關鍵字全大寫,非關鍵字全小寫。實際使用沒有這個要求。
SQL的JOIN會用,用得好,能使SQL准確取到想取的數據,同時SQL語句還結構清晰易維護。它的通常形式為:
SELECT <結果字段集> FROM <左表> JOIN <右表> ON <連接條件> WHERE <篩選條件>
其中的JOIN可以換成以下的這些
JOIN, INNER JOIN, LEFT JOIN, RIGHT JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL JOIN, FULL OUTER JOIN, CROSS JOIN
但上面有些是等價的,一些是另一些的簡寫,整理如下:
全寫 | 簡寫 |
INNER JOIN | JOIN |
LEFT OUTER JOIN | LEFT JOIN |
RIGHT OUTER JOIN | RIGHT JOIN |
FULL OUTER JOIN | FULL JOIN |
CROSS JOIN | (無) |
除了CROSS JOIN無須有、也不能有ON之外,其它的JOIN都必須有ON。
關於這些JOIN的基本用法,網上已經不少文章介紹,可以先看看 這個,這個 或者百度一下“SQL連接JOIN”。
下文假設你已經明白這些JOIN的基本用法。
哪個表算左,哪個表算右?
如果是兩表,則XX JOIN左邊的表算左,右邊的表算右,與ON后面的條件的寫法無關。比如ON后面是等於號,等於號兩邊互換,不影響左右表的認定,也不影響查詢結果。
如果是多表,則XX JOIN左邊的全部表算左,右邊緊跟的那一個表算右,與ON后面的條件寫法無關。
ON后面如果有多個條件,會如何影響查詢的結果?
如果有多個條件,只看整個表達式的結果是True還是False,與部分表達式是否成立無關。比如 ON a.f1 = b.f1 AND a.f2 = b.f2,假如a.f1不等於b.f1,則不管a.f2是否等於b.f2,整個ON表達式都對查詢的結果無影響。ON后面不管多復雜,只看表達式整體運算的結果。
容易犯的一個誤區(重點來啦!)
以LEFT JOIN舉例,一些人認為LEFT JOIN從左表出發得到的結果,(假設沒有WHERE語句),左表的一行在結果中有且只有一行。
基於ON的關聯條件,結果分成3種情況:
1. 左表的1行,對應右表0行,則結果是1行,其中右表字段都為NULL
2. 左表的1行,對應右表1行,則結果是1行。
3. 左表的1行,對應右表n行(n>1),則結果是n行,其中左表字段被復制到結果中的n行中。
結論:
- 對於LEFT JOIN,左表的一行,在結果中也可能出現多行!
- RIGHT JOIN同理:對於RIGHT JOIN,右表的一行,在結果中也可能出現多行!
- 其它的JOIN都會在對面(左的對面是右)的表有多條數據符合ON條件時在結果中出現多次。
- 沒有ON的CROSS JOIN,天生就會出現多行,左右表在結果中都可能出現多行!
當需要用FULL JOIN,但當前的數據庫又不支持怎么辦?
比如MySQL就不支持FULL JOIN。
這個網上有通用的解法,即是把FULL JOIN轉化成(LEFT JOIN) UNION (RIGHT JOIN)的結構。
支持FULL JOIN的寫法:
SELECT * FROM table_a AS a FULL JOIN table_b AS b ON a.a_id = b.a_id
不支持FULL JOIN的寫法:
SELECT * FROM table_a AS a LEFT JOIN table_b AS b ON a.a_id = b.a_id UNION SELECT * FROM table_a AS a RIGHT JOIN table_b AS b ON a.a_id = b.a_id
但是實際SQL往往不會這么簡單。當table_a或table_b是一個子查詢(可能SQL很長)時,被寫成兩處,就會帶來維護的不便和兩處修改可能不一致的風險。
SELECT * FROM ( SELECT * FROM table_a -- 想象這是這是一個語句很長、很復雜的子查詢 ) AS a LEFT JOIN ( SELECT * FROM table_b -- 想象這是這是另一個語句很長、很復雜的子查詢 ) AS b ON a.a_id = b.a_id UNION SELECT * FROM ( SELECT * FROM table_a -- 想象這是這是一個語句很長、很復雜的子查詢 ) AS a LEFT JOIN ( SELECT * FROM table_b -- 想象這是這是另一個語句很長、很復雜的子查詢 ) AS b ON a.a_id = b.a_id
改成下面這樣的寫法就能避免這個問題:
WITH a AS ( SELECT * FROM table_a -- 想象這是這是一個語句很長、很復雜的子查詢 ), b AS ( SELECT * FROM table_b -- 想象這是這是另一個語句很長、很復雜的子查詢 ) SELECT * FROM a LEFT JOIN b ON a.a_id = b.a_id UNION SELECT * FROM a RIGHT JOIN b ON a.a_id = b.a_id
現在你是不是對JOIN的認知加深了一些?有不清楚歡迎留言。