MySQL 支援 SELECT
陳述式和多表格 DELETE
和 UPDATE
陳述式的 table_references
部分的下列 JOIN
語法
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
的語法有所擴展。標準 SQL 只接受 table_reference
,而不接受括號內的一系列 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
等。如果在使用聯結條件時將逗號聯結與其他聯結類型混合使用,可能會發生Unknown column '
形式的錯誤。有關處理此問題的資訊將在本節稍後提供。col_name
' in 'on clause'與
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
或LEFT JOIN
,其USING
子句會命名兩個表格中存在的所有欄位。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
在內部聯結中,
COALESCE(a.c1, b.c1)
與a.c1
或b.c1
相同,因為這兩個欄位具有相同的值。在外部聯結(例如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
中的欄位,而不是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 的一個擴展是,MySQL 允許您限定
NATURAL
或USING
聯結的通用(合併)欄位,而標準不允許這樣做。