

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# JOIN 子句
<a name="join-clause"></a>

SQL JOIN 子句用于根据公共字段合并两个或多个表中的数据。根据指定的联接方法，结果可能会发生变化，也可能不发生变化。当在其他表中找不到匹配项时，左外部联接和右外部联接保留某个已联接表中的值。

JOIN 类型和连接条件的组合决定了哪些行包含在最终结果集中。然后，SELECT 和 WHERE 子句控制返回哪些列以及如何筛选行。了解不同的 JOIN 类型以及如何有效使用它们是 SQL 中的一项关键技能，因为它允许你以灵活而强大的方式合并来自多个表的数据。

## 语法
<a name="join-clause-syntax"></a>

```
SELECT column1, column2, ..., columnn
FROM table1
join_type table2
ON table1.column = table2.column;
```

## 参数
<a name="join-clause-parameters"></a>

 *选择第 1 列、第 2 列、...、第 N 列*   
要包含在结果集中的列。您可以从 JOIN 中涉及的两个表中的一个或两个表中选择列。

 *来自表 1*   
JOIN 操作中的第一个（左）表。

 *[JOIN \$1 INNER JOIN \$1 左 [外] 连接 \$1 右 [外部] 联接 \$1 完整 [外部] 联接] table2：*  
要执行的 JOIN 类型。JOIN 或 INNER JOIN 仅返回两个表中值匹配的行。  
LEFT [OUTER] JOIN 返回左表中的所有行，以及右表中的匹配行。  
RIGHT [OUTER] JOIN 返回右表中的所有行，以及左表中的匹配行。  
FULL [OUTER] JOIN 返回两个表中的所有行，无论是否存在匹配项。  
CROSS JOIN 创建两个表中行的笛卡尔乘积。

 *ON table 1.column = table 2.*  
连接条件，它指定如何匹配两个表中的行。连接条件可以基于一列或多列。

 *WHERE 状况：*  
一个可选子句，可用于根据指定条件进一步筛选结果集。

## 示例
<a name="Join_examples"></a>

以下示例是使用 USING 子句在两个表之间进行的联接。在这种情况下，列 listid 和 eventid 用作联接列。结果限制为 5 行。

```
select listid, listing.sellerid, eventid, listing.dateid, numtickets
from listing join sales
using (listid, eventid)
order by 1
limit 5;

listid | sellerid | eventid | dateid | numtickets
-------+----------+---------+--------+-----------
1      | 36861    | 7872    | 1850   | 10
4      | 8117     | 4337    | 1970   | 8
5      | 1616     | 8647    | 1963   | 4
5      | 1616     | 8647    | 1963   | 4
6      | 47402    | 8240    | 2053   | 18
```

# 联接类型
<a name="join-types"></a>

## INNER
<a name="inner-join"></a>

这是默认的联接类型。返回两个表引用中具有匹配值的行。

INNER JOIN 是 SQL 中最常用的联接类型。这是一种基于公共列或一组列合并来自多个表的数据的强大方法。

**语法：**

```
SELECT column1, column2, ..., columnn
FROM table1
INNER JOIN table2
ON table1.column = table2.column;
```

以下查询将返回客户表和订单表之间存在匹配的 customer\$1id 值的所有行。结果集将包含客户编号、姓名、订单编号和订单日期列。

```
SELECT customers.customer_id, customers.name, orders.order_id, orders.order_date
FROM customers
INNER JOIN orders
ON customers.customer_id = orders.customer_id;
```

下面的查询是 LISTING 表和 SALES 表之间的内部联接（不带 JOIN 关键字），其中 LISTING 表中的 LISTID 介于 1 和 5 之间。此查询匹配 LISTING 表（左表）和 SALES 表（右表）中的 LISTID 列值。结果显示 LISTID 1、4 和 5 符合条件。

```
select listing.listid, sum(pricepaid) as price, sum(commission) as comm
from listing, sales
where listing.listid = sales.listid
and listing.listid between 1 and 5
group by 1
order by 1;

listid | price  |  comm
-------+--------+--------
     1 | 728.00 | 109.20
     4 |  76.00 |  11.40
     5 | 525.00 |  78.75
```

以下示例是与 ON 子句的内部联接。在这种情况下，不返回 NULL 行。

```
select listing.listid, sum(pricepaid) as price, sum(commission) as comm
from sales join listing
on sales.listid=listing.listid and sales.eventid=listing.eventid
where listing.listid between 1 and 5
group by 1
order by 1;

listid | price  |  comm
-------+--------+--------
     1 | 728.00 | 109.20
     4 |  76.00 |  11.40
     5 | 525.00 |  78.75
```

以下查询是 FROM 子句中的两个子查询的内部联接。此查询查找不同类别的活动（音乐会和演出）的已售门票数和未售门票数：这些 FROM 子句子查询是*表* 子查询；它们可返回多个列和行。

```
select catgroup1, sold, unsold
from
(select catgroup, sum(qtysold) as sold
from category c, event e, sales s
where c.catid = e.catid and e.eventid = s.eventid
group by catgroup) as a(catgroup1, sold)
join
(select catgroup, sum(numtickets)-sum(qtysold) as unsold
from category c, event e, sales s, listing l
where c.catid = e.catid and e.eventid = s.eventid
and s.listid = l.listid
group by catgroup) as b(catgroup2, unsold)

on a.catgroup1 = b.catgroup2
order by 1;

catgroup1 |  sold  | unsold
----------+--------+--------
Concerts  | 195444 |1067199
Shows     | 149905 | 817736
```

## 左 [外]
<a name="left-outer-join"></a>

返回左表引用中的所有值和右表引用中的匹配值，如果没有匹配项，则附加 NULL。它也称为*左外连接*。

它返回左（第一个）表中的所有行，以及右表（第二个）中的匹配行。如果右表中没有匹配项，则结果集将包含右表中各列的 NULL 值。可以省略 OUTER 关键字，连接可以简单地写成 LEFT JOIN。与 LEFT OUTER JOIN 相反的是右外连接，它返回右表中的所有行和左表中的匹配行。

**语法：**

```
SELECT column1, column2, ..., columnn
FROM table1
LEFT [OUTER] JOIN table2
ON table1.column = table2.column;
```

以下查询将返回客户表中的所有行，以及订单表中的匹配行。如果客户没有订单，结果集仍将包含该客户的信息，order\$1id 和 order\$1date 列的值为空。

```
SELECT customers.customer_id, customers.name, orders.order_id, orders.order_date
FROM customers
LEFT OUTER JOIN orders
ON customers.customer_id = orders.customer_id;
```

以下查询是一个左外部联接。当在其他表中找不到匹配项时，左外部联接和右外部联接保留某个已联接表中的值。左表和右表是语法中列出的第一个表和第二个表。NULL 值用于填补结果集中的“空白”。此查询匹配 LISTING 表（左表）和 SALES 表（右表）中的 LISTID 列值。结果显示， LISTIDs 2和3没有带来任何销售。

```
select listing.listid, sum(pricepaid) as price, sum(commission) as comm
from listing left outer join sales on sales.listid = listing.listid
where listing.listid between 1 and 5
group by 1
order by 1;

listid | price  |  comm
-------+--------+--------
     1 | 728.00 | 109.20
     2 | NULL   | NULL
     3 | NULL   | NULL
     4 |  76.00 |  11.40
     5 | 525.00 |  78.75
```

## 右 [外部]
<a name="right-outer-join"></a>

返回右表引用中的所有值和左表引用中的匹配值，如果没有匹配项，则附加 NULL。它也称为*右外连接*。

它返回右（第二个）表中的所有行，以及左表（第一个）中的匹配行。如果左表中没有匹配项，则结果集将包含左表中各列的 NULL 值。可以省略 OUTER 关键字，连接可以简单地写成 RIGHT JOIN。与 RIGHT OUTER JOIN 相反的是 LEFT OUTER JOIN，它返回左表中的所有行和右表中的匹配行。

**语法：**

```
SELECT column1, column2, ..., columnn
FROM table1
RIGHT [OUTER] JOIN table2
ON table1.column = table2.column;
```

以下查询将返回客户表中的所有行，以及订单表中的匹配行。如果客户没有订单，结果集仍将包含该客户的信息，order\$1id 和 order\$1date 列的值为空。

```
SELECT orders.order_id, orders.order_date, customers.customer_id, customers.name
FROM orders
RIGHT OUTER JOIN customers
ON orders.customer_id = customers.customer_id;
```

以下查询是一个右外部联接。此查询匹配 LISTING 表（左表）和 SALES 表（右表）中的 LISTID 列值。结果显示 LISTIDs 1、4 和 5 符合标准。

```
select listing.listid, sum(pricepaid) as price, sum(commission) as comm
from listing right outer join sales on sales.listid = listing.listid
where listing.listid between 1 and 5
group by 1
order by 1;

listid | price  |  comm
-------+--------+--------
     1 | 728.00 | 109.20
     4 |  76.00 |  11.40
     5 | 525.00 |  78.75
```

## 完整 [外部]
<a name="full-join"></a>

返回两个关系中的所有值，在不匹配的一侧附加 NULL 值。它也被称为*完全外连接*。

无论是否存在匹配项，它都会返回左表和右表中的所有行。如果没有匹配项，则结果集将包含表中没有匹配行的列的 NULL 值。可以省略 OUTER 关键字，连接可以简单地写成 FULL JOIN。FULL OUTER JOIN 不如左外联接或右外联接那么常用，但在某些情况下，即使没有匹配项，也需要查看两个表中的所有数据，它可能很有用。

**语法：**

```
SELECT column1, column2, ..., columnn
FROM table1
FULL [OUTER] JOIN table2
ON table1.column = table2.column;
```

以下查询将返回客户表和订单表中的所有行。如果客户没有订单，结果集仍将包含该客户的信息，order\$1id 和 order\$1date 列的值为空。如果订单没有关联客户，则结果集将包括该订单，customer\$1id 和名称列的值为空。

```
SELECT customers.customer_id, customers.name, orders.order_id, orders.order_date
FROM customers
FULL OUTER JOIN orders
ON customers.customer_id = orders.customer_id;
```

以下查询是一个完全联接。当在其他表中找不到匹配项时，完全联接保留已联接表中的值。左表和右表是语法中列出的第一个表和第二个表。NULL 值用于填补结果集中的“空白”。此查询匹配 LISTING 表（左表）和 SALES 表（右表）中的 LISTID 列值。结果显示， LISTIDs 2和3没有带来任何销售。

```
select listing.listid, sum(pricepaid) as price, sum(commission) as comm
from listing full join sales on sales.listid = listing.listid
where listing.listid between 1 and 5
group by 1
order by 1;

listid | price  |  comm
-------+--------+--------
     1 | 728.00 | 109.20
     2 | NULL   | NULL
     3 | NULL   | NULL
     4 |  76.00 |  11.40
     5 | 525.00 |  78.75
```

以下查询是一个完全联接。此查询匹配 LISTING 表（左表）和 SALES 表（右表）中的 LISTID 列值。只有未产生任何销售额的行（LISTIDs 2 和 3）才会出现在结果中。

```
select listing.listid, sum(pricepaid) as price, sum(commission) as comm
from listing full join sales on sales.listid = listing.listid
where listing.listid between 1 and 5
and (listing.listid IS NULL or sales.listid IS NULL)
group by 1
order by 1;

listid | price  |  comm
-------+--------+--------
     2 | NULL   | NULL
     3 | NULL   | NULL
```

## [左] 半
<a name="left-semi-join"></a>

从表格引用的左侧返回与右侧匹配的值。它也被称为*左半联接*。

它只返回左表（第一个）中右表（第二个）中具有匹配行的行。它不返回右表中的任何列，只返回左表中的列。当您想在一个表中查找另一个表中具有匹配项的行，而无需返回第二个表中的任何数据时，LEFT SEMI JOIN 非常有用。与使用带有 IN 或 EXISTS 子句的子查询相比，LEFT SEMI JOIN 是一种更有效的替代方案。

**语法：**

```
SELECT column1, column2, ..., columnn
FROM table1
LEFT SEMI JOIN table2
ON table1.column = table2.column;
```

对于订单表中至少有一个订单的客户，以下查询将仅返回客户表中的 customer\$1id 和姓名列。结果集将不包括订单表中的任何列。

```
SELECT customers.customer_id, customers.name
FROM customers
LEFT SEMI JOIN orders
ON customers.customer_id = orders.customer_id;
```

## CROSS JOIN
<a name="cross-join"></a>

返回两个关系的笛卡尔乘积。这意味着结果集将包含两个表中所有可能的行组合，不应用任何条件或筛选器。

当您需要从两个表中生成所有可能的数据组合时，例如在创建显示客户和产品信息的所有可能组合的报告时，CROSS JOIN非常有用。CROSS JOIN 与其他联接类型（INNER JOIN、LEFT JOIN 等）不同，因为它在 ON 子句中没有连接条件。交叉联接不需要连接条件。

**语法：**

```
SELECT column1, column2, ..., columnn
FROM table1
CROSS JOIN table2;
```

以下查询将返回一个结果集，其中包含客户和产品表中客户编号、客户名称、产品编号和产品名称的所有可能组合。如果客户表有 10 行，产品表有 20 行，则 CROSS JOIN 的结果集将包含 10 x 20 = 200 行。

```
SELECT customers.customer_id, customers.name, products.product_id, products.product_name
FROM customers
CROSS JOIN products;
```

以下查询是 LISTING 表和 SALES 表的交叉联接或笛卡尔联接，其中包含限制结果的谓词。此查询匹配 SALES 表和 LISTID 表中两个表中 LISTIDs 1、2、3、4 和 5 的 LISTID 列值。结果显示 20 个行符合条件。

```
select sales.listid as sales_listid, listing.listid as listing_listid
from sales cross join listing
where sales.listid between 1 and 5
and listing.listid between 1 and 5
order by 1,2;

sales_listid | listing_listid
-------------+---------------
1            | 1
1            | 2
1            | 3
1            | 4
1            | 5
4            | 1
4            | 2
4            | 3
4            | 4
4            | 5
5            | 1
5            | 1
5            | 2
5            | 2
5            | 3
5            | 3
5            | 4
5            | 4
5            | 5
5            | 5
```

## 反加入
<a name="anti-join"></a>

返回左表引用中与右表引用不匹配的值。它也被称为*左反连接*。

当您想在一个表中查找另一个表中没有匹配项的行时，ANTI JOIN 是一个非常有用的操作。

**语法：**

```
SELECT column1, column2, ..., columnn
FROM table1
LEFT ANTI JOIN table2
ON table1.column = table2.column;
```

以下查询将返回所有未下任何订单的客户。

```
SELECT customers.customer_id, customers.name
FROM customers
LEFT ANTI JOIN orders
ON customers.customer_id = orders.customer_id
WHERE orders.order_id IS NULL;
```

## NATURAL
<a name="natural-join"></a>

指定两个关系中的行将隐式匹配所有名称相匹配的列。

它会自动匹配两个表之间具有相同名称和数据类型的列。它不需要您在 ON 子句中明确指定连接条件。它将两个表之间的所有匹配列合并到结果集中。

当您要联接的表中包含具有相同名称和数据类型的列时，NATURAL JOIN 是一种便捷的简写形式。但是，通常建议使用更明确的 INNER JOIN... ON 语法使连接条件更明确、更易于理解。

**语法：**

```
SELECT column1, column2, ..., columnn
FROM table1
NATURAL JOIN table2;
```

以下示例是两个表`employees`和`departments`与以下列之间的自然联接：
+ `employees`表:`employee_id`,`first_name`,`last_name`, `department_id ` 
+ `departments`表:`department_id`, `department_name ` 

以下查询将根据`department_id`列返回一个结果集，其中包括两个表之间所有匹配行的名字、姓氏和部门名称。

```
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
NATURAL JOIN departments d;
```

以下示例是两个表之间的自然联接。在这种情况下，列 listid、sellerid、eventid 和 dateid 在两个表中具有相同的名称和数据类型，因此用作联接列。结果限制为 5 行。

```
select listid, sellerid, eventid, dateid, numtickets
from listing natural join sales
order by 1
limit 5;

listid | sellerid  | eventid | dateid | numtickets
-------+-----------+---------+--------+-----------
113    | 29704     | 4699    | 2075   | 22
115    | 39115     | 3513    | 2062   | 14
116    | 43314     | 8675    | 1910   | 28
118    | 6079      | 1611    | 1862   | 9
163    | 24880     | 8253    | 1888   | 14
```