MySQL SELECT語法(三)JOIN語法詳解

  源自MySQL 5.7 官方手冊:13.2.9.2 JOIN Syntaxhtml

SELECT select_expr
From table_references JOIN...
WHERE...

  如上所示,MySQL支持在table_references後添加JOIN選項做爲SELECT語句的一部分,固然也能夠在多表的DELETE和UPDATE。mysql

  下面列出了JOIN的詳細語法:sql

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]
  | table_subquery [AS] alias
  | ( table_references )

joined_table:
    table_reference [INNER | CROSS] JOIN table_factor [join_specification]
  | table_reference STRAIGHT_JOIN table_factor
  | table_reference STRAIGHT_JOIN table_factor ON search_condition
  | table_reference {LEFT|RIGHT} [OUTER] JOIN table_reference join_specification
  | table_reference NATURAL [{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] ...

 

1、表引用(table reference)數據庫

  一個表引用也被稱爲一個JOIN表達式。表引用(當它引用分區表時)可能有PARTITION選項,包括一個由逗號分隔的分區,子分區或二者皆有的列表。此選項緊跟在的名字以後,並在任何別名聲明以前。此選項的做用是僅從列出的分區或子分區中選擇數據行,並且將忽略列表中未命名的任何分區或子分區。see Section 22.5, 「Partition Selection」。測試

  table_factor語法是MySQL對標準SQL中的擴展。標準SQL只接受table_reference,而不是一對括號內的列表。優化

  若是table_reference項列表中的每一個逗號被視爲內鏈接(INNER JOIN),則這是保守的擴展。例如:spa

SELECT * FROM t1 LEFT JOIN (t2, t3, t4)
                 ON (t2.a = t1.a AND t3.b = t1.b AND t4.c = t1.c)

等價於:code

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搭配其它。htm

  通常來講,在只有INNER JOIN操做的表達式中,括號能夠被省略。MySQL還支持嵌套鏈接,blog

See Section 8.2.1.7, 「Nested Join Optimization」。

 

  指定索引提示(Index  hints )可以影響MySQL優化器如何使用索引。更多信息,see Section 8.9.4, 「Index Hints」.

  優化器提示和optimizer_switch系統變量是影響優化器使用索引的其餘方法。See Section 8.9.3, 「Optimizer Hints」, and Section 8.9.2, 「Switchable Optimizations」。

 

2、在編寫聯接時要考慮的通常因素

  2.1 

  可使用tbl_name AS alias_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;

  2.2 

  table_subquery也稱爲FROM子句中的派生表或子查詢。Section 13.2.10.8, 「Derived Tables」. 

  此類子查詢必須包含別名,以便爲子查詢結果提供表名。一個簡單的例子以下:

SELECT * FROM (SELECT 1, 2, 3) AS t1;

/*

+---+---+---+
| 1 | 2 | 3 |
+---+---+---+
| 1 | 2 | 3 |
+---+---+---+

*/

  2.3

  在沒有鏈接條件的狀況下,INNER JOIN和「,」(逗號)在語義上是等效的——二者都在指定的表之間產生笛卡爾積,也就是說,第一個表中的每一行都鏈接到第二個表中的每一行。

  可是,逗號運算符的優先級比其它含有「JOIN」的運算符要小。若是在存在鏈接條件時將逗號鏈接與其餘鏈接類型混合,則可能會報錯:Unknown column 'col_name' in 'on clause' 。對這個問題的處理會在文章的後面討論。

  與ON一塊兒使用的search_condition是能夠在WHERE子句中使用的任何條件表達式。ON子句用於指明如多表如何鏈接,WHERE子句則限制要包含在結果集中的行。

 

  2.4

  在LEFT JOIN中,若是在右表中沒有匹配ON或者USING中條件的行,則該鏈接中中的右表的列全都設置爲NULL。你能夠利用這點來查找左表A中在右表B中沒有任何對應項的行:

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的ID列中不存在。See Section 8.2.1.8, 「Outer Join Optimization」.(外鏈接包括LEFT JOIN和RIGHT JOIN)

  例如,我查找學生表stu中在成績表sc中沒有任何成績的學生:

select stu.* 
from student as stu left join sc on stu.SId=sc.SId
where sc.SId is null; /* SId | Sname | Sage | Ssex | +------+-------+---------------------+------+ | 09 | 張三 | 2017-12-20 00:00:00 | 女 | | 10 | 李四 | 2017-12-25 00:00:00 | 女 | | 11 | 李四 | 2017-12-30 00:00:00 | 女 | | 12 | 趙六 | 2017-01-01 00:00:00 | 女 | | 13 | 孫七 | 2018-01-01 00:00:00 | 女 | +------+-------+---------------------+------+ */

  固然這裏碰到了一個小問題,把查詢語句的WHERE條件改爲sc.SId=null時,取出的是空集:

select stu.* 
from student as stu left join sc on stu.SId=sc.SId 
where sc.SId=null;

/*

Empty set (0.08 sec)

*/

  在WHERE子句中,column = null永遠不會爲true,以這種方式使用null無效,要檢測值爲NULL的列,必須使用IS NULL或列IS NOT NULL。關於NULL的使用有專門的章節:Working with NULL Values。

 

  2.5 

  USING(join_column_list)子句指定兩個表中必須擁有的列的列表。若是表a和b都包含列c1,c2和c3,則如下鏈接將比較兩個表中的相應列:

a LEFT JOIN b USING (c1, c2, c3)

  2.6 

  兩個表的NATURAL [LEFT] JOIN等下於下面的狀況:帶有USING子句的INNER JOIN或LEFT JOIN,該子句列出了在兩個表中都存在的全部的列。

  2.7

  RIGHT JOIN的工做方式相似於LEFT JOIN。爲了使代碼能夠跨數據庫移植,建議您使用LEFT JOIN而不是RIGHT JOIN。

  2.8

  語法描述中的{ 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語法。

 

  2.9

  STRAIGHT_JOIN相似於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;

  Natural join和使用USING的JOIN,包括外鏈接的變體,是根據SQL-2003的標準進行處理的。

  

  2.10

  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」列,都只會出現一次:

/*

+------+------+------+
| j    | i    | k    |
+------+------+------+
|    1 |    1 |    1 |
+------+------+------+

+------+------+------+
| j    | i    | k    |
+------+------+------+
|    1 |    1 |    1 |
+------+------+------+

*/

  冗餘列的消除和列的排序都是根據標準SQL進行處理,按下面的順序展現:

  • 首先,合併兩個鏈接表的相同列,按他們在第一個表中出現的順序排列;
  • 而後,第一個表所特有的列,按它們在該表中出現的順序排列;
  • 第三,第二個表所特有的列,它們在該表中出現的順序;

  取代兩個表的相同列的單列是經過使用coalesce(合併)操做來定義的,也就是說,對於兩個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)

  

  若是任何其餘的join操做,則鏈接的結果列由參與鏈接的表的全部列的串聯組成。合併的列的定義結果是,對於外鏈接,若是兩列中的一列始終爲NULL,則合併列包含非NULL列的值。若是兩列都不爲NULL或者都爲NULL,兩個公共列具備相同的值,所以選擇哪一列做爲合併列的值就可有可無了。解釋這一點的一種簡單方法是考慮外鏈接的合併列由JOIN的內部表的公共列表示。

  假設表t1(a,b)和t2(a,c)具備如下內容:

/*

t1    t2
----  ----
1 x   2 z
2 y   3 w

*/

  那麼下面這個JOIN,列a包含的是t1.a的值:

SELECT * FROM t1 NATURAL LEFT JOIN t2;

+------+------+------+
| a    | b    | c    |
+------+------+------+
|    1 | x    | NULL |
|    2 | y    | z    |
+------+------+------+

 

  而下面的JOIN,剛好相反,a列包含的是t2.a的值:

SELECT * FROM t1 NATURAL RIGHT JOIN t2;
+------+------+------+ | a | c | b | +------+------+------+ | 2 | z | y | | 3 | w | NULL | +------+------+------+

  將這些結果與JOIN ... ON的等效查詢進行比較:

 SELECT * FROM t1 LEFT JOIN t2 ON (t1.a = t2.a);

+------+------+------+------+
| a    | b    | a    | c    |
+------+------+------+------+
|    1 | x    | NULL | NULL |
|    2 | y    |    2 | z    |
+------+------+------+------+
SELECT * FROM t1 RIGHT JOIN t2 ON (t1.a = t2.a);

+------+------+------+------+
| a    | b    | a    | c    |
+------+------+------+------+
|    2 | y    |    2 | z    |
| NULL | NULL |    3 | w    |
+------+------+------+------+

 

  2.11

  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的JOIN,SELECT *選擇這些值:

COALESCE(a.c1, b.c1), COALESCE(a.c2, b.c2), COALESCE(a.c3, b.c3)

  而使用ON的JOIN,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。該列會從結果中略去。

 

  2.12

  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;

  執這個SELECT語句會報錯:Unknown column 'i3' in 'on clause' ,由於i3是t3中的一列,它不是ON子句的操做數。

  對此語句進行修改:

SELECT * FROM t1 JOIN t2 JOIN t3 ON (i1 = i3);

  對ON的做用範圍進行測試,如下語句均能執行:

SELECT * FROM t1 JOIN t2 JOIN t3 ON (i2 = i3);
Empty set (0.00 sec)

SELECT * FROM t1 JOIN t2 JOIN t3 ON (i1 = i2);
Empty set (0.00 sec)

  即ON對其以前的JOIN中的表的列都能引用。

 

  2.13

  JOIN比逗號操做符擁有更高的優先級,因此下面這個表達式:

t1, t2 JOIN t3

  會被解釋爲:

 (t1, (t2 JOIN t3))

  而不是:

((t1, t2) JOIN t3)

  這個特色會影響使用ON子句的語句,由於ON子句只能引用JOIN操做的表中的列,優先級會影響對這些操做表的解釋。執行以下的語句就報錯了:

SELECT * FROM t1,t2 JOIN t3 ON (i1 = i2);

ERROR 1054 (42S22): Unknown column 'i1' in 'on clause'

  而這樣就能成功執行:

SELECT * FROM (t1,t2) JOIN t3 ON (i1 = i2);

Empty set (0.00 sec)

  或者不適用逗號:

SELECT * FROM t1 join t2 JOIN t3 ON (i1 = i2);

Empty set (0.00 sec)

  此外,INNER JOIN,CROSS JOIN,LEFT JOIN和RIGHT JOIN混合的語句中,全部這些語句的優先級都高於逗號運算符。

 

  2.14

  與SQL:2003標準相比,MySQL擴展是MySQL容許您限定NATURAL或USING鏈接的公共(coalesced合併)列,而標準SQL不容許這樣作。

相關文章
相關標籤/搜索