MySQL 支援以下 JOIN
語法,用於 SELECT
陳述式的 table_references
部分,以及多表格 DELETE
和 UPDATE
陳述式
table_references:
escaped_table_reference [, escaped_table_reference] ...
escaped_table_reference: {
table_reference
| { OJ table_reference }
}
table_reference: {
table_factor
| joined_table
}
table_factor: {
tbl_name [PARTITION (partition_names)]
[[AS] alias] [index_hint_list]
| [LATERAL] table_subquery [AS] alias [(col_list)]
| ( table_references )
}
joined_table: {
table_reference {[INNER | CROSS] JOIN | STRAIGHT_JOIN} table_factor [join_specification]
| table_reference {LEFT|RIGHT} [OUTER] JOIN table_reference join_specification
| table_reference NATURAL [INNER | {LEFT|RIGHT} [OUTER]] JOIN table_factor
}
join_specification: {
ON search_condition
| USING (join_column_list)
}
join_column_list:
column_name [, column_name] ...
index_hint_list:
index_hint [, index_hint] ...
index_hint: {
USE {INDEX|KEY}
[FOR {JOIN|ORDER BY|GROUP BY}] ([index_list])
| {IGNORE|FORCE} {INDEX|KEY}
[FOR {JOIN|ORDER BY|GROUP BY}] (index_list)
}
index_list:
index_name [, index_name] ...
表格參考也稱為聯結表示式。
表格參考 (當它參考分割表格時) 可能包含 PARTITION
子句,包含以逗號分隔的分割區、子分割區或兩者的清單。此選項位於表格名稱之後,且在任何別名宣告之前。此選項的效果是僅從列出的分割區或子分割區中選取資料列。清單中未命名的任何分割區或子分割區都會被忽略。如需更多資訊和範例,請參閱 第 26.5 節,「分割區選取」。
與標準 SQL 相比,MySQL 中 table_factor
的語法已擴充。標準僅接受 table_reference
,而不是括號內的一系列項目。
如果 table_reference
項目清單中的每個逗號都被視為等同於內部聯結,這是一個保守的擴充。例如
SELECT * FROM t1 LEFT JOIN (t2, t3, t4)
ON (t2.a = t1.a AND t3.b = t1.b AND t4.c = t1.c)
等同於
SELECT * FROM t1 LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4)
ON (t2.a = t1.a AND t3.b = t1.b AND t4.c = t1.c)
在 MySQL 中,JOIN
、CROSS JOIN
和 INNER JOIN
在語法上是等效的 (它們可以互相取代)。在標準 SQL 中,它們並不等效。INNER JOIN
與 ON
子句一起使用,CROSS JOIN
則在其他情況下使用。
一般而言,在僅包含內部聯結運算的聯結表示式中,可以忽略括號。MySQL 也支援巢狀聯結。請參閱 第 10.2.1.8 節,「巢狀聯結最佳化」。
可以指定索引提示來影響 MySQL 最佳化工具如何使用索引。如需更多資訊,請參閱 第 10.9.4 節,「索引提示」。最佳化工具提示和 optimizer_switch
系統變數是影響最佳化工具使用索引的其他方法。請參閱 第 10.9.3 節,「最佳化工具提示」和 第 10.9.2 節,「可切換最佳化」。
以下清單描述了撰寫聯結時要考慮的一般因素
可以使用
或tbl_name
ASalias_name
tbl_name alias_name
為表格參考建立別名。SELECT t1.name, t2.salary FROM employee AS t1 INNER JOIN info AS t2 ON t1.name = t2.name; SELECT t1.name, t2.salary FROM employee t1 INNER JOIN info t2 ON t1.name = t2.name;
table_subquery
也稱為衍生表格或FROM
子句中的子查詢。請參閱 第 15.2.15.8 節,「衍生表格」。這類子查詢必須包含別名,以給予子查詢結果一個表格名稱,並且可以選擇性地包含括號中的表格欄名稱清單。以下是一個簡單的範例SELECT * FROM (SELECT 1, 2, 3) AS t1;
單一聯結中可以參考的最大表格數量為 61 個。這包括透過將
FROM
子句中的衍生表格和檢視合併到外部查詢區塊所處理的聯結 (請參閱 第 10.2.2.4 節,「使用合併或實體化來最佳化衍生表格、檢視參考和通用表格表示式」)。在沒有聯結條件的情況下,
INNER JOIN
和,
(逗號) 在語義上是等效的:兩者都會在指定的表格之間產生笛卡爾乘積 (也就是說,第一個表格中的每一行都會聯結到第二個表格中的每一行)。但是,逗號運算子的優先順序低於
INNER JOIN
、CROSS JOIN
、LEFT JOIN
等。如果在存在聯結條件時將逗號聯結與其他聯結類型混合使用,可能會發生'on clause'
中出現Unknown column '
形式的錯誤。本節稍後將提供有關處理此問題的資訊。col_name
'與
ON
一起使用的search_condition
是任何可以於WHERE
子句中使用的條件表示式。一般而言,ON
子句用於指定如何聯結表格的條件,而WHERE
子句則限制在結果集中要包含哪些資料列。如果
LEFT JOIN
的ON
或USING
部分中沒有右表格的相符資料列,則會使用所有欄位都設為NULL
的資料列作為右表格。您可以使用此事實來尋找表格中在另一個表格中沒有對應項的資料列SELECT left_tbl.* FROM left_tbl LEFT JOIN right_tbl ON left_tbl.id = right_tbl.id WHERE right_tbl.id IS NULL;
此範例會尋找
left_tbl
中id
值在right_tbl
中不存在的所有資料列 (也就是說,left_tbl
中所有在right_tbl
中沒有對應資料列的資料列)。請參閱 第 10.2.1.9 節,「外部聯結最佳化」。USING(
子句命名必須存在於兩個表格中的欄位清單。如果表格join_column_list
)a
和b
都包含欄位c1
、c2
和c3
,則以下聯結會比較兩個表格中的對應欄位a LEFT JOIN b USING (c1, c2, c3)
兩個表格的
NATURAL [LEFT] JOIN
定義為在語義上等同於INNER JOIN
或具有USING
子句的LEFT JOIN
,該子句會命名兩個表格中存在的所有欄位。RIGHT JOIN
的運作方式類似於LEFT JOIN
。為了保持程式碼在不同資料庫之間的可攜性,建議您使用LEFT JOIN
而不是RIGHT JOIN
。聯結語法描述中顯示的
{ OJ ... }
語法僅為了與 ODBC 相容。語法中的大括號應按字面寫入;它們不是語法描述中其他地方使用的元語法。SELECT left_tbl.* FROM { OJ left_tbl LEFT OUTER JOIN right_tbl ON left_tbl.id = right_tbl.id } WHERE right_tbl.id IS NULL;
您可以在
{ OJ ... }
中使用其他類型的聯結,例如INNER JOIN
或RIGHT OUTER JOIN
。這有助於與某些協力廠商應用程式相容,但並非官方的 ODBC 語法。STRAIGHT_JOIN
類似於JOIN
,不同之處在於一律先讀取左表格,然後再讀取右表格。這可以用於聯結最佳化工具以次最佳順序處理表格的 (少數) 情況。
一些聯結範例
SELECT * FROM table1, table2;
SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id;
SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id;
SELECT * FROM table1 LEFT JOIN table2 USING (id);
SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id
LEFT JOIN table3 ON table2.id = table3.id;
自然聯結和使用 USING
的聯結 (包括外部聯結變體) 會根據 SQL:2003 標準進行處理
NATURAL
聯結的冗餘欄位不會出現。請考慮以下陳述式集CREATE TABLE t1 (i INT, j INT); CREATE TABLE t2 (k INT, j INT); INSERT INTO t1 VALUES(1, 1); INSERT INTO t2 VALUES(1, 1); SELECT * FROM t1 NATURAL JOIN t2; SELECT * FROM t1 JOIN t2 USING (j);
在第一個
SELECT
陳述式中,欄位j
出現在兩個表格中,因此成為聯結欄位,因此根據標準 SQL,它應該只在輸出中出現一次,而不是兩次。同樣地,在第二個 SELECT 陳述式中,欄位j
在USING
子句中命名,因此應該只在輸出中出現一次,而不是兩次。因此,這些陳述式會產生以下輸出
+------+------+------+ | j | i | k | +------+------+------+ | 1 | 1 | 1 | +------+------+------+ +------+------+------+ | j | i | k | +------+------+------+ | 1 | 1 | 1 | +------+------+------+
冗餘欄位消除和欄位排序會根據標準 SQL 發生,產生此顯示順序
首先,兩個聯結表格的合併通用欄位,按照它們在第一個表格中發生的順序
其次,第一個表格獨有的欄位,按照它們在該表格中發生的順序
第三,第二個表格獨有的欄位,按照它們在該表格中發生的順序
使用合併運算定義取代兩個通用欄位的單一結果欄位。也就是說,對於兩個
t1.a
和t2.a
,產生的單一聯結欄位a
定義為a = COALESCE(t1.a, t2.a)
,其中COALESCE(x, y) = (CASE WHEN x IS NOT NULL THEN x ELSE y END)
如果聯結運算是任何其他聯結,則聯結的結果欄位由聯結表格的所有欄位的串連組成。
合併欄位定義的一個後果是,對於外部聯結,如果兩個欄位之一始終為
NULL
,則合併欄位包含非NULL
欄位的值。如果兩個欄位皆非或皆為NULL
,則兩個通用欄位的值相同,因此選擇哪一個作為合併欄位的值並不重要。解釋此情況的一種簡單方法是將外部聯結的合併欄位視為由JOIN
的內部表格的通用欄位所表示。假設表格t1(a, b)
和t2(a, c)
具有以下內容t1 t2 ---- ---- 1 x 2 z 2 y 3 w
然後,對於此聯結,欄位
a
包含t1.a
的值mysql> SELECT * FROM t1 NATURAL LEFT JOIN t2; +------+------+------+ | a | b | c | +------+------+------+ | 1 | x | NULL | | 2 | y | z | +------+------+------+
相較之下,對於此聯結,欄位
a
包含t2.a
的值。mysql> SELECT * FROM t1 NATURAL RIGHT JOIN t2; +------+------+------+ | a | c | b | +------+------+------+ | 2 | z | y | | 3 | w | NULL | +------+------+------+
將這些結果與使用
JOIN ... ON
的其他等效查詢進行比較mysql> SELECT * FROM t1 LEFT JOIN t2 ON (t1.a = t2.a); +------+------+------+------+ | a | b | a | c | +------+------+------+------+ | 1 | x | NULL | NULL | | 2 | y | 2 | z | +------+------+------+------+
mysql> SELECT * FROM t1 RIGHT JOIN t2 ON (t1.a = t2.a); +------+------+------+------+ | a | b | a | c | +------+------+------+------+ | 2 | y | 2 | z | | NULL | NULL | 3 | w | +------+------+------+------+
USING
子句可以改寫為比較對應欄位的ON
子句。但是,雖然USING
和ON
類似,但它們並不完全相同。請考慮以下兩個查詢a LEFT JOIN b USING (c1, c2, c3) a LEFT JOIN b ON a.c1 = b.c1 AND a.c2 = b.c2 AND a.c3 = b.c3
關於判斷哪些資料列滿足聯結條件,這兩個聯結在語義上是相同的。
關於判斷要為
SELECT *
展開顯示哪些欄位,這兩個聯結在語義上並不相同。USING
聯結選取對應欄位的合併值,而ON
聯結選取所有表格的所有欄位。對於USING
聯結,SELECT *
選取以下值COALESCE(a.c1, b.c1), COALESCE(a.c2, b.c2), COALESCE(a.c3, b.c3)
對於
ON
聯結,SELECT *
選取以下值a.c1, a.c2, a.c3, b.c1, b.c2, b.c3
使用內部聯結(inner join)時,
COALESCE(a.c1, b.c1)
的結果等同於a.c1
或b.c1
,因為這兩個欄位的值相同。使用外部聯結(outer join,例如LEFT JOIN
)時,其中一個欄位的值可能為NULL
。該欄位會從結果中省略。ON
子句只能參考其運算元。範例
CREATE TABLE t1 (i1 INT); CREATE TABLE t2 (i2 INT); CREATE TABLE t3 (i3 INT); SELECT * FROM t1 JOIN t2 ON (i1 = i3) JOIN t3;
此陳述式會因
Unknown column 'i3' in 'on clause'
錯誤而失敗,因為i3
是t3
中的一個欄位,而t3
並非ON
子句的運算元。若要讓聯結能夠處理,請將此陳述式重寫如下:SELECT * FROM t1 JOIN t2 JOIN t3 ON (i1 = i3);
JOIN
的優先順序高於逗號運算子(,
),因此聯結運算式t1, t2 JOIN t3
會被解讀為(t1, (t2 JOIN t3))
,而不是((t1, t2) JOIN t3)
。這會影響使用ON
子句的陳述式,因為該子句只能參考聯結運算元中的欄位,而優先順序會影響對這些運算元的解讀。範例
CREATE TABLE t1 (i1 INT, j1 INT); CREATE TABLE t2 (i2 INT, j2 INT); CREATE TABLE t3 (i3 INT, j3 INT); INSERT INTO t1 VALUES(1, 1); INSERT INTO t2 VALUES(1, 1); INSERT INTO t3 VALUES(1, 1); SELECT * FROM t1, t2 JOIN t3 ON (t1.i1 = t3.i3);
JOIN
的優先順序高於逗號運算子,因此ON
子句的運算元為t2
和t3
。由於t1.i1
並非任一運算元中的欄位,因此會導致Unknown column 't1.i1' in 'on clause'
錯誤。若要讓聯結能夠處理,請使用下列其中一種策略:
使用括號明確地將前兩個資料表分組,使
ON
子句的運算元為(t1, t2)
和t3
SELECT * FROM (t1, t2) JOIN t3 ON (t1.i1 = t3.i3);
避免使用逗號運算子,改用
JOIN
SELECT * FROM t1 JOIN t2 JOIN t3 ON (t1.i1 = t3.i3);
相同的優先順序解讀也適用於混合使用逗號運算子與
INNER JOIN
、CROSS JOIN
、LEFT JOIN
和RIGHT JOIN
的陳述式,這些運算子的優先順序都高於逗號運算子。與 SQL:2003 標準相比,MySQL 的擴充功能允許您限定
NATURAL
或USING
聯結的通用(合併)欄位,而標準則不允許這樣做。