文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (美式信紙尺寸) - 39.9Mb
PDF (A4 尺寸) - 40.0Mb
Man Pages (TGZ) - 258.5Kb
Man Pages (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


15.2.13.2 JOIN 子句

MySQL 支援以下 JOIN 語法,用於 SELECT 陳述式的 table_references 部分,以及多表格 DELETEUPDATE 陳述式

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 中,JOINCROSS JOININNER JOIN 在語法上是等效的 (它們可以互相取代)。在標準 SQL 中,它們並不等效。INNER JOINON 子句一起使用,CROSS JOIN 則在其他情況下使用。

一般而言,在僅包含內部聯結運算的聯結表示式中,可以忽略括號。MySQL 也支援巢狀聯結。請參閱 第 10.2.1.8 節,「巢狀聯結最佳化」

可以指定索引提示來影響 MySQL 最佳化工具如何使用索引。如需更多資訊,請參閱 第 10.9.4 節,「索引提示」。最佳化工具提示和 optimizer_switch 系統變數是影響最佳化工具使用索引的其他方法。請參閱 第 10.9.3 節,「最佳化工具提示」第 10.9.2 節,「可切換最佳化」

以下清單描述了撰寫聯結時要考慮的一般因素

  • 可以使用 tbl_name AS alias_nametbl_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 JOINCROSS JOINLEFT JOIN 等。如果在存在聯結條件時將逗號聯結與其他聯結類型混合使用,可能會發生 'on clause' 中出現 Unknown column 'col_name' 形式的錯誤。本節稍後將提供有關處理此問題的資訊。

  • ON 一起使用的 search_condition 是任何可以於 WHERE 子句中使用的條件表示式。一般而言,ON 子句用於指定如何聯結表格的條件,而 WHERE 子句則限制在結果集中要包含哪些資料列。

  • 如果 LEFT JOINONUSING 部分中沒有右表格的相符資料列,則會使用所有欄位都設為 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_tblid 值在 right_tbl 中不存在的所有資料列 (也就是說,left_tbl 中所有在 right_tbl 中沒有對應資料列的資料列)。請參閱 第 10.2.1.9 節,「外部聯結最佳化」

  • USING(join_column_list) 子句命名必須存在於兩個表格中的欄位清單。如果表格 ab 都包含欄位 c1c2c3,則以下聯結會比較兩個表格中的對應欄位

    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 JOINRIGHT 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 陳述式中,欄位 jUSING 子句中命名,因此應該只在輸出中出現一次,而不是兩次。

    因此,這些陳述式會產生以下輸出

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

    冗餘欄位消除和欄位排序會根據標準 SQL 發生,產生此顯示順序

    • 首先,兩個聯結表格的合併通用欄位,按照它們在第一個表格中發生的順序

    • 其次,第一個表格獨有的欄位,按照它們在該表格中發生的順序

    • 第三,第二個表格獨有的欄位,按照它們在該表格中發生的順序

    使用合併運算定義取代兩個通用欄位的單一結果欄位。也就是說,對於兩個 t1.at2.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 子句。但是,雖然 USINGON 類似,但它們並不完全相同。請考慮以下兩個查詢

    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.c1b.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' 錯誤而失敗,因為 i3t3 中的一個欄位,而 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 子句的運算元為 t2t3。由於 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 JOINCROSS JOINLEFT JOINRIGHT JOIN 的陳述式,這些運算子的優先順序都高於逗號運算子。

  • 與 SQL:2003 標準相比,MySQL 的擴充功能允許您限定 NATURALUSING 聯結的通用(合併)欄位,而標準則不允許這樣做。