MySQL 的 Left join、Right join 和 Inner join 的区别原创
在 MySQL 中,LEFT JOIN、RIGHT JOIN 和 INNER JOIN 是常用的三种表连接方式,它们的主要区别在于结果集包含的记录范围。以下是详细的区别及各自的使用场景:
# 1. INNER JOIN(内连接)
- 定义:只返回两张表中符合连接条件的记录。
- 结果集:仅包含在两张表中都有匹配的记录。
- 场景:用于获取两个表中都存在的相关数据。
# 示例:
SELECT
a.id, a.name, b.order_id
FROM
customers a
INNER JOIN
orders b
ON
a.id = b.customer_id;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 结果:
customers表和orders表中,a.id = b.customer_id的匹配记录。
# 特点:
- 如果记录在任意一张表中没有匹配,则不会出现在结果集中。
- 执行效率较高,因为只处理匹配的记录。
# 2. LEFT JOIN(左连接)
- 定义:返回左表(
LEFT)中的所有记录,即使右表中没有匹配的记录。 - 结果集:包括左表中所有的记录和右表中匹配的记录。如果右表中没有匹配,则右表字段为
NULL。 - 场景:用于需要保留左表所有数据的场景,即使没有对应的右表数据。
# 示例:
SELECT
a.id, a.name, b.order_id
FROM
customers a
LEFT JOIN
orders b
ON
a.id = b.customer_id;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 结果:
customers表中的所有记录,右表中没有匹配时,b.order_id显示为NULL。
# 特点:
- 确保左表的数据完整性。
- 常用于找出左表中没有匹配右表的记录。
# 3. RIGHT JOIN(右连接)
- 定义:返回右表(
RIGHT)中的所有记录,即使左表中没有匹配的记录。 - 结果集:包括右表中所有的记录和左表中匹配的记录。如果左表中没有匹配,则左表字段为
NULL。 - 场景:用于需要保留右表所有数据的场景,即使没有对应的左表数据。
# 示例:
SELECT
a.id, a.name, b.order_id
FROM
customers a
RIGHT JOIN
orders b
ON
a.id = b.customer_id;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 结果:
orders表中的所有记录,左表中没有匹配时,a.id和a.name显示为NULL。
# 特点:
- 确保右表的数据完整性。
- 在实际应用中使用频率较低,可以通过调整表顺序和使用
LEFT JOIN达到相同效果。
# 区别总结
| 特性 | INNER JOIN | LEFT JOIN | RIGHT JOIN |
|---|---|---|---|
| 匹配规则 | 只返回两表匹配的记录 | 返回左表所有记录及匹配项 | 返回右表所有记录及匹配项 |
| 左表保留 | 否 | 是 | 否 |
| 右表保留 | 否 | 否 | 是 |
| 不匹配时 | 不返回 | 右表字段为 NULL | 左表字段为 NULL |
| 使用场景 | 获取交集 | 保留左表数据完整性 | 保留右表数据完整性 |
# 4. 可视化理解
假设我们有两张表:
# customers(客户表)
| id | name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 3 | Charlie |
# orders(订单表)
| order_id | customer_id |
|---|---|
| 101 | 1 |
| 102 | 3 |
| 103 | 4 |
# 查询结果:
INNER JOIN:- 匹配
customers.id = orders.customer_id - 结果:
id name order_id 1 Alice 101 3 Charlie 102
- 匹配
LEFT JOIN:- 保留
customers表中的所有记录。 - 结果:
id name order_id 1 Alice 101 2 Bob NULL 3 Charlie 102
- 保留
RIGHT JOIN:- 保留
orders表中的所有记录。 - 结果:
id name order_id 1 Alice 101 3 Charlie 102 NULL NULL 103
- 保留
# 5. 最常见的坑:LEFT JOIN 被 WHERE 悄悄变成 INNER JOIN
这是 join 里最容易出错、且不会报错的一个陷阱。以下两条语句只差一个位置,结果完全不同:
-- 写法 A:条件放在 WHERE
SELECT a.id, a.name, b.order_id
FROM customers a
LEFT JOIN orders b ON a.id = b.customer_id
WHERE b.order_id > 100;
-- 写法 B:条件放在 ON
SELECT a.id, a.name, b.order_id
FROM customers a
LEFT JOIN orders b ON a.id = b.customer_id AND b.order_id > 100;
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
用上面 customers / orders 的数据,写法 A 的结果里 Bob 消失了:
| id | name | order_id |
|---|---|---|
| 1 | Alice | 101 |
| 3 | Charlie | 102 |
写法 B 才保留了 Bob:
| id | name | order_id |
|---|---|---|
| 1 | Alice | 101 |
| 2 | Bob | NULL |
| 3 | Charlie | 102 |
原因:LEFT JOIN 先产生带 NULL 的结果集,WHERE 再对这个结果集过滤。Bob 那行的 b.order_id 是 NULL,而 NULL > 100 的结果是 NULL(不是 TRUE),该行被过滤掉——LEFT JOIN 就退化成了 INNER JOIN。
规则:对右表的过滤条件写进 ON,对左表的过滤条件写进 WHERE。
唯一的例外是刻意利用这个行为找「左表有、右表没有」的记录,此时必须显式判 NULL:
-- 找出从未下过单的客户
SELECT a.id, a.name
FROM customers a
LEFT JOIN orders b ON a.id = b.customer_id
WHERE b.customer_id IS NULL;
1
2
3
4
5
2
3
4
5
# 6. 选择使用的关键
INNER JOIN:用于只需要匹配的数据。LEFT JOIN:需要保留左表所有记录,即使没有匹配项。RIGHT JOIN:需要保留右表所有记录(实际使用中较少,交换表顺序改用LEFT JOIN更易读)。- 对右表的过滤放
ON,对左表的过滤放WHERE,否则LEFT JOIN会退化成INNER JOIN。
join 的执行方式与索引选择见MySQL执行计划。
上次更新: 8/7/2026
- 02
- ORDER BY 配合 LIMIT 触发的索引选择陷阱 原创08-07
- 03
- Nginx 运维知识地图:从配置基础到反向代理实战 原创07-29