#### 13.2.10.2 JOIN 子句

[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)

MySQL支持以下`加入`的语法*`表参考`*部分[`选择`](select.html)语句和多表[`删除`](delete.html)和[`更新`](update.html)声明：

```
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] ...
```

表引用也称为连接表达式。

表引用（当它引用分区表时）可能包含一个`划分`子句，包括逗号分隔的分区、子分区或两者的列表。此选项跟在表的名称之后，并在任何别名声明之前。此选项的效果是仅从列出的分区或子分区中选择行。任何未在列表中命名的分区或子分区都将被忽略。有关更多信息和示例，请参阅[第 24.5 节，“分区选择”](partitioning-selection.html).

的语法*`表因子`*与标准 SQL 相比，在 MySQL 中进行了扩展。该标准只接受*`表参考`*，而不是一对括号内的列表。

这是一个保守的扩展，如果列表中的每个逗号*`表参考`*items 被认为等同于内部连接。例如：

```
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 中，`加入`,`交叉连接`， 和`内部联接`是句法等价物（它们可以相互替换）。在标准 SQL 中，它们是不等价的。`内部联接`与一个`在`条款，`交叉连接`以其他方式使用。

通常，在仅包含内连接操作的连接表达式中可以忽略括号。MySQL 还支持嵌套连接。看[第 8.2.1.8 节，“嵌套连接优化”](nested-join-optimization.html).

可以指定索引提示来影响 MySQL 优化器如何使用索引。有关详细信息，请参阅[第 8.9.4 节，“索引提示”](index-hints.html).优化器提示和`优化器开关`系统变量是影响优化器使用索引的其他方式。看[第 8.9.3 节，“优化器提示”](optimizer-hints.html)， 和[第 8.9.2 节，“可切换的优化”](switchable-optimizations.html).

以下列表描述了编写联接时要考虑的一般因素：

-   表引用可以使用别名`*`tbl_name`* 作为 *`别名`*`要么*`tbl_名称别名_名称`*:

    ```
    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;
    ```

-   A.*`表_子查询`*在中也称为派生表或子查询`从…起`条款看见[第13.2.11.8节，“衍生表格”](derived-tables.html).这样的子查询*必须*包含一个别名，为子查询结果提供一个表名，还可以选择在括号中包含一个表列名列表。下面是一个简单的例子：

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

-   单个联接中可引用的最大表数为61。这包括通过在中合并派生表和视图来处理的联接`从…起`子句转换为外部查询块（请参见[第8.2.2.4节，“通过合并或物化优化派生表、视图引用和公共表表达式”](derived-table-optimization.html)).

    [](<>)[](<>)

-   `内部连接`和`,`（逗号）在没有连接条件的情况下在语义上是等价的：两者在指定的表之间产生笛卡尔乘积（即，第一个表中的每一行都连接到第二个表中的每一行）。

    但是，逗号运算符的优先级低于`内部连接`,`交叉连接`,`左连接`等等当存在连接条件时，如果将逗号连接与其他连接类型混合使用，则会出现形式错误`未知列'*`上校的名字`*在“on子句”中`可能发生。本节后面将提供有关处理此问题的信息。

-   这个*`搜索条件`*用于`在…上`是可在中使用的形式的任何条件表达式`哪里`条款一般来说`在…上`子句用于指定如何联接表的条件，以及`哪里`子句限制要包含在结果集中的行。

-   如果在`在…上`或`使用`参与`左连接`，所有列都设置为`无效的`用于正确的桌子。您可以使用此事实查找表中其他表中没有对应行的行：

    ```
    SELECT left_tbl.*
      FROM left_tbl LEFT JOIN right_tbl ON left_tbl.id = right_tbl.id
      WHERE right_tbl.id IS NULL;
    ```

    本例查找中的所有行`左_tbl`带着`身份证件`不存在于`对`（也就是说，所有行`左_tbl`中没有对应的行`对`).看到了吗[第8.2.1.9节，“外部连接优化”](outer-join-optimization.html).

-   这个`使用(*`加入列列表`*)`子句指定两个表中必须存在的列的列表。如果桌子`A.`和`b`两者都包含列`c1`, `c2`和`c3`，以下联接将比较两个表中的相应列：

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

-   这个`自然[左]连接`两个表的类型定义为语义等价于`内部连接`或者`左连接`用一个`使用`子句，该子句命名两个表中存在的所有列。

-   `右键连接`类似于`左连接`.为了使代码在数据库之间保持可移植性，建议您使用`左连接`而不是`右键连接`.

-   [](<>) [](<>)这个`{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…}`例如`内部连接`或`右外接`。这有助于与某些第三方应用程序兼容，但不是官方的ODBC语法。

-   `直联`类似于`参加`，除了左表总是在右表之前读取。这可以用于联接优化器以次优顺序处理表的（少数）情况。

    例如：


```
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;
```

自然连接和连接`使用`，包括外部联接变体，根据SQL:2003标准进行处理：

-   a的冗余列`自然的`加入不会出现。考虑一下这组语句：

    ```
    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.html)声明，专栏`j`出现在两个表中，因此成为联接列，因此，根据标准SQL，它应该只在输出中出现一次，而不是两次。类似地，在第二条SELECT语句中`j`被命名为`使用`子句，并且应该只在输出中出现一次，而不是两次。

    因此，这些语句产生以下输出：

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

    根据标准SQL进行冗余列消除和列排序，生成以下显示顺序：

    -   首先，按照它们在第一个表中出现的顺序合并两个联接表的公共列

    -   第二，第一个表特有的列，按它们在该表中出现的顺序排列

    -   第三，第二个表特有的列，按它们在该表中出现的顺序排列

        替换两个公共列的单个结果列是使用合并操作定义的。也就是说，两个人`t1。A.`和`t2。A.`生成的单联接列`A.`定义为`a=聚结（t1.a，t2.a）`，其中：

    ```
    COALESCE(x, y) = (CASE WHEN x IS NOT NULL THEN x ELSE y END)
    ```

    如果联接操作是任何其他联接，则联接的结果列由联接表的所有列的串联组成。

    合并列定义的结果是，对于外部联接，合并列包含非-`无效的`如果两列中的一列始终为`无效的`.如果两列或两列都不匹配`无效的`，两个公共列都有相同的值，因此选择哪一个作为合并列的值并不重要。解释这一点的一个简单方法是考虑外部联接的合并列由A的内部表的公共列表示。`参加`1.假设桌子`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 |
    +------+------+------+
    ```

    将这些结果与其他等效查询进行比较`参加在…上`:

    ```
    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    |
    +------+------+------+------+
    ```

-   A.`使用`子句可以重写为`在…上`子句来比较相应的列。然而，尽管`使用`和`在…上`它们很相似，但并不完全相同。考虑以下两个查询：

    [](<>)[](<>)[](<>)

    ```
    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
    ```

    关于确定哪些行满足连接条件，两个连接在语义上是相同的。

    关于确定要为哪些列显示`挑选*`扩展后，这两个连接在语义上并不相同。这个`使用`join选择相应列的合并值，而`在…上`join从所有表中选择所有列。对于`使用`参加`挑选*`选择以下值：

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

    对于`在…上`参加`挑选*`选择以下值：

    ```
    a.c1, a.c2, a.c3, b.c1, b.c2, b.c3
    ```

    通过内部连接，[`合并（a.c1，b.c1）`](comparison-operators.html#function_coalesce)都一样`a、 c1`或`b、 c1`因为两列的值相同。带有外部连接（例如`左连接`)，两列中的一列可以是`无效的`.结果中省略了那一列。

-   一`在…上`子句只能引用其操作数。

    例子：

    ```
    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;
    ```

    该声明以错误告终`“on子句”中的未知列“i3”`错误是因为`i3`是一个专栏吗`t3`，它不是`在…上`条款要处理联接，请按如下方式重写语句：

    [](<>)

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

-   `参加`具有比逗号运算符更高的优先级(`,`)所以join表达式`t1，t2加入t3`被解释为`（t1，（t2和t3））`，而不是`（（t1，t2）连接t3）`。这会影响使用`在…上`子句，因为该子句只能引用联接的操作数中的列，并且优先级会影响对这些操作数的解释。

    例子：

    ```
    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);
    ```

    这个`参加`优先于逗号运算符，因此`在…上`条款是`t2`和`t3`因为`t1。i1`不是任一操作数中的列，结果是`未知列't1。i1“在”on“子句中”`错误

    [](<>)

    要处理连接，请使用以下任一策略：

    -   用括号将前两个表显式分组，以便`在…上`条款是`（t1，t2）`和`t3`:

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

    -   避免使用逗号运算符，并使用`参加`相反：

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

        同样的优先级解释也适用于将逗号运算符与`内部连接`, `交叉连接`, `左连接`和`右键连接`，所有这些运算符的优先级都高于逗号运算符。

-   与SQL:2003标准相比，MySQL的一个扩展是，MySQL允许您限定`自然的`或`使用`加入，而标准不允许这样做。
