MySQL的SQL語句 - 數據操做語句(12)- SELECT 語句(3)

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 聯接的公共(合併)列,而標準不容許這樣作。

官方網址:
https://dev.mysql.com/doc/refman/8.0/en/join.html

相關文章
相關標籤/搜索