JOIN 子句html
MySQL 對 SELECT 語句和多表 DELETE 和 UPDATE 語句 table_references 部分支持如下 JOIN 語法: 1. table_references: 2. escaped_table_reference [, escaped_table_reference] ... 3. 4. escaped_table_reference: { 5. table_reference 6. | { OJ table_reference } 7. } 8. 9. table_reference: { 10. table_factor 11. | joined_table 12. } 13. 14. table_factor: { 15. tbl_name [PARTITION (partition_names)] 16. [[AS] alias] [index_hint_list] 17. | [LATERAL] table_subquery [AS] alias [(col_list)] 18. | ( table_references ) 19. } 20. 21. joined_table: { 22. table_reference {[INNER | CROSS] JOIN | STRAIGHT_JOIN} table_factor [join_specification] 23. | table_reference {LEFT|RIGHT} [OUTER] JOIN table_reference join_specification 24. | table_reference NATURAL [INNER | {LEFT|RIGHT} [OUTER]] JOIN table_factor 25. } 26. 27. join_specification: { 28. ON search_condition 29. | USING (join_column_list) 30. } 31. 32. join_column_list: 33. column_name [, column_name] ... 34. 35. index_hint_list: 36. index_hint [, index_hint] ... 37. 38. index_hint: { 39. USE {INDEX|KEY} 40. [FOR {JOIN|ORDER BY|GROUP BY}] ([index_list]) 41. | {IGNORE|FORCE} {INDEX|KEY} 42. [FOR {JOIN|ORDER BY|GROUP BY}] (index_list) 43. } 44. 45. index_list: 46. index_name [, index_name] ...
表引用也稱爲聯接表達式。mysql
表引用(當它引用分區表時)能夠包含 PARTITION 選項,包括逗號分隔的分區、子分區列表。此選項緊跟在表名以後,並位於任何別名以前。這個選項的效果是隻從列出的分區或子分區中選擇行。忽略列表中未命名的任何分區或子分區。sql
與標準 SQL 相比,MySQL 擴展了 table_factor 的語法。標準 SQL 只接受 table_reference,而不接受一對括號內的列表。數據庫
若是 table_reference 項目列表中的每一個逗號都被視爲等同於內部聯接,則這是一個保守的擴展。例如:ide
1. SELECT * FROM t1 LEFT JOIN (t2, t3, t4) 2. ON (t2.a = t1.a AND t3.b = t1.b AND t4.c = t1.c)
等價於:優化
1. SELECT * FROM t1 LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4) 2. 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。code
一般,在只包含內部聯接操做的聯接表達式中能夠忽略圓括號。MySQL 還支持嵌套鏈接。htm
能夠指定索引提示來影響 MySQL 優化器如何使用索引。排序
下表描述了寫聯接語句時要考慮的通常因素:索引
● 可使用 tbl_name AS alias_name 或 tbl_name alias_name 對錶引用使用別名:
1. SELECT t1.name, t2.salary 2. FROM employee AS t1 INNER JOIN info AS t2 ON t1.name = t2.name; 3. 4. SELECT t1.name, t2.salary 5. FROM employee t1 INNER JOIN info t2 ON t1.name = t2.name;
● table_subquery 在 FROM 子句中也稱爲派生表或子查詢。此類子查詢必須包含別名,以便爲子查詢結果提供表名,而且能夠選擇在括號中包含表的列名。下面是一個簡單的例子:
1. SELECT * FROM (SELECT 1, 2, 3) AS t1;
● 單個聯接中可引用的最大表數量爲 61。這包括經過將 FROM 子句中的派生表和視圖合併到外部查詢塊來處理的聯接。
● 在沒有鏈接條件的狀況下,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。可使用此事實來查找表中在另外一個表中沒有對應項的行:
1. SELECT left_tbl.* 2. FROM left_tbl LEFT JOIN right_tbl ON left_tbl.id = right_tbl.id 3. WHERE right_tbl.id IS NULL;
此示例查找 left_tbl 中 id 值不在 right_tbl 中的全部行(也就是說,left_tbl 中的全部行在 right butl 都中沒有對應的行)。
● USING(join_column_list) 子句指定兩個表中必須存在的列名的列表。若是表a和表b都包含列c一、c2和c3,則下面的聯接將比較兩個表中的相應列:
1. a LEFT JOIN b USING (c1, c2, c3)
● 兩個表的 NATURAL [LEFT] JOIN 被定義爲語義上等價於 INNER JOIN 或帶有 USING 子句的 LEFT JOIN,USING 子句命名兩個表中存在的全部列。
● RIGHT JOIN 的工做原理與 LEFT JOIN 相似。爲了保持代碼在數據庫之間的可移植性,建議使用 LEFT JOIN 而不是 RIGHT JOIN。
● 聯接語法描述中顯示的 { OJ ... } 語法僅用於與 ODBC 兼容。語法中的大括號應該按字面意思寫;它們不是語法描述中其餘地方使用的元語法。
1. SELECT left_tbl.* 2. FROM { OJ left_tbl LEFT OUTER JOIN right_tbl 3. ON left_tbl.id = right_tbl.id } 4. WHERE right_tbl.id IS NULL;
能夠在 { OJ ... } 中使用其餘類型的鏈接,例如 INNER JOIN 或 RIGHT OUTER JOIN。這有助於與某些第三方應用程序兼容,但不是官方的 ODBC 語法。
● STRAIGHT_JOIN 相似於 JOIN,只是左表老是在右表以前讀取。這能夠用於鏈接優化器以次優順序處理表的那些(少數)狀況。
一些鏈接示例:
1. SELECT * FROM table1, table2; 2. 3. SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id; 4. 5. SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id; 6. 7. SELECT * FROM table1 LEFT JOIN table2 USING (id); 8. 9. SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id 10. LEFT JOIN table3 ON table2.id = table3.id;
天然聯接和使用 USING 的聯接,包括外部聯接變體,根據 SQL:2003 標準處理:
● NATURAL 聯接的冗餘列不會出現。參考這組語句:
1. CREATE TABLE t1 (i INT, j INT); 2. CREATE TABLE t2 (k INT, j INT); 3. INSERT INTO t1 VALUES(1, 1); 4. INSERT INTO t2 VALUES(1, 1); 5. SELECT * FROM t1 NATURAL JOIN t2; 6. SELECT * FROM t1 JOIN t2 USING (j);
在第一個 SELECT 語句中,j 列出如今兩個表中,所以成爲一個聯接列,所以,根據標準 SQL,它應該只在輸出中出現一次,而不是兩次。相似地,在第二個 SELECT 語句中,列 j 在 USING 子句中命名,而且應該只在輸出中出現一次,而不是兩次。
所以,這些語句產生如下輸出:
1. +------+------+------+ 2. | j | i | k | 3. +------+------+------+ 4. | 1 | 1 | 1 | 5. +------+------+------+ 6. +------+------+------+ 7. | j | i | k | 8. +------+------+------+ 9. | 1 | 1 | 1 | 10. +------+------+------+
根據標準 SQL 進行冗餘列消除和列排序,生成如下顯示順序:
■ 首先,按照它們在第一個表中出現的順序,合併兩個鏈接表的公共列
■ 第二,按照它們在該表中出現的順序排列第一個表所特有的列
■ 第三,按照它們在該表中出現的順序排列第二個表所特有的列
替換兩個公共列,得到單個結果列是使用合併操做定義的。也就是說,對於兩個 t1.a 和 t2.a,生成的單個聯接列 a 定義爲 a = COALESCE(t1.a, t2.a),其中:
1. COALESCE(x, y) = (CASE WHEN x IS NOT NULL THEN x ELSE y END)
若是聯接操做是任何其餘聯接,則聯接的結果列由聯接表的全部列的組成。
合併列定義的結果是,對於外部聯接,若是兩個列中的一個始終爲 NULL,則合併列包含非 NULL 列的值。若是兩列都爲 NULL 或都不爲 NULL,則兩個公共列都具備相同的值,所以選擇哪一列做爲合併列的值可有可無。解釋這種狀況的一種簡單方法是,考慮外部聯接的合併列由聯接的內部表的公共列表示。假設表 t1(a, b) 和 t2(a, c) 具備如下內容:
1.t1 t2 2.---- ---- 3.1 x 2 z 4.2 y 3 w
而後,對於這個鏈接,a 列包含 t1.a 的值:
1.mysql> SELECT * FROM t1 NATURAL LEFT JOIN t2; 2. +------+------+------+ 3. | a | b | c | 4. +------+------+------+ 5. | 1 | x | NULL | 6. | 2 | y | z | 7. +------+------+------+
相比之下,對於這個鏈接,列 a 包含 t2.a 的值。
1. 1. mysql> SELECT * FROM t1 NATURAL RIGHT JOIN t2 ON (t1.a = t2.a); 2. +------+------+------+------+ 3. | a | b | a | c | 4. +------+------+------+------+ 5. | 1 | x | NULL | NULL | 6. | 2 | y | 2 | z | 7. +------+------+------+------+
1.mysql> SELECT * FROM t1 RIGHT JOIN t2 ON (t1.a = t2.a); 2.+------+------+------+------+ 3.| a | b | a | c | 4.+------+------+------+------+ 5.| 2 | y | 2 | z | 6.| NULL | NULL | 3 | w | 7.+------+------+------+------+
● USING 子句能夠重寫爲比較相應列的 ON 子句。然而,儘管 USING 和 ON 是類似的,但它們並不徹底相同。考慮如下兩個查詢:
1. a LEFT JOIN b USING (c1, c2, c3) 2.a LEFT JOIN b ON a.c1 = b.c1 AND a.c2 = b.c2 AND a.c3 = b.c3
關於肯定哪些行知足聯接條件,兩個聯接在語義上是相同的。
關於肯定要爲 SELECT 顯示哪些列,這兩個聯接在語義上是不相同的。USING 聯接選擇相應列的合併值,而 ON 聯接選擇全部表中的全部列。對於 USING 聯接,SELECT 選擇如下值:
1.COALESCE(a.c1, b.c1), COALESCE(a.c2, b.c2), 2.COALESCE(a.c3, b.c3)
對於 ON 聯接,SELECT * 選擇如下值:
1.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 子句只能引用其操做數。
例子:
1.CREATE TABLE t1 (i1 INT); 2.CREATE TABLE t2 (i2 INT); 3.CREATE TABLE t3 (i3 INT); 4.SELECT * FROM t1 JOIN t2 ON (i1 = i3) JOIN t3;
語句失敗並出現 Unknown column 'i3' in 'on clause' 錯誤,由於 i3 是 t3 中的列,而 t3 不是 ON 子句的操做數。要使聯接可以被處理,請重寫語句,以下所示:
1.SELECT * FROM t1 JOIN t2 JOIN t3 ON (i1 = i3);
● JOIN 的優先級高於逗號運算符 (,),所以鏈接表達式 t1, t2 JOIN t3 解釋爲 (t1, (t2 JOIN t3)),而不是 ((t1, t2) JOIN t3)。這會影響使用 ON 子句的語句,由於該子句只能引用聯接操做數中的列,優先級會影響對這些操做數的解析。
例子:
1.CREATE TABLE t1 (i1 INT, j1 INT); 2. CREATE TABLE t2 (i2 INT, j2 INT); 3. CREATE TABLE t3 (i3 INT, j3 INT); 4. INSERT INTO t1 VALUES(1, 1); 5. INSERT INTO t2 VALUES(1, 1); 6. INSERT INTO t3 VALUES(1, 1); 7. 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:
1. SELECT * FROM (t1, t2) JOIN t3 ON (t1.i1 = t3.i3);
■ 避免使用逗號運算符,改用 JOIN:
1. SELECT * FROM t1 JOIN t2 JOIN t3 ON (t1.i1 = t3.i3);
一樣的優先級解釋也適用於將逗號運算符與 INNER JOIN、CROSS JOIN、LEFT JOIN 和 RIGHT JOIN 混合使用的語句,這些語句的優先級都高於逗號運算符。
● MySQL 擴展了 SQL:2003 標準,容許限定天然聯接或 USING 聯接的公共(合併)列,而標準不容許這樣作。