以下是 SQL 外连接的完整讲解,包含INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL JOIN 以及内、外连接的核心概念,全部用表格举例说明。
一、内连接 vs 外连接核心区别
表格
复制
| 对比项 | 内连接 (INNER JOIN) | 外连接 (LEFT/RIGHT/FULL JOIN) |
|---|---|---|
| 匹配规则 | 只返回两表匹配的行 | 返回匹配行 + 未匹配行(补 NULL) |
| 结果集大小 | ≤ 两表匹配行数 | ≥ 内连接结果(可能更大) |
| 未匹配数据处理 | 丢弃 | 保留,缺失侧补 NULL |
| 使用场景 | 只关心两表都有的数据 | 需要保留某表全部数据 |
| 图示 | 两圆交集 | 左圆 / 右圆 / 两圆全部 |
二、示例数据表
employees 员工表(左表)
表格
| emp_id | emp_name | dept_id |
|---|---|---|
| 1 | 张三 | 10 |
| 2 | 李四 | 20 |
| 3 | 王五 | 10 |
| 4 | 赵六 | NULL |
| 5 | 孙七 | 50 |
departments 部门表(右表)
表格
| dept_id | dept_name |
|---|---|
| 10 | 技术部 |
| 20 | 销售部 |
| 30 | 财务部 |
三、INNER JOIN(内连接)回顾
sql
SELECT e.emp_name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;
结果:
表格
| emp_name | dept_name | 说明 |
|---|---|---|
| 张三 | 技术部 | 匹配 |
| 王五 | 技术部 | 匹配 |
| 李四 | 销售部 | 匹配 |
未匹配的行被丢弃:赵六(dept_id=NULL)、孙七(dept_id=50 无对应部门)、财务部(无员工)
四、LEFT JOIN(左外连接)
语法
sql
SELECT e.emp_name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id;
规则
表格
| 规则 | 说明 |
|---|---|
| 保留左表全部行 | employees 表所有 5 条记录都出现 |
| 右表匹配不上 | 补 NULL |
| 右表有多条匹配 | 左表行重复显示 |
结果
表格
| emp_name | dept_name | 来源分析 |
|---|---|---|
| 张三 | 技术部 | 左表 + 右表匹配 |
| 王五 | 技术部 | 左表 + 右表匹配 |
| 李四 | 销售部 | 左表 + 右表匹配 |
| 赵六 | NULL | 左表有,右表无匹配(dept_id=NULL) |
| 孙七 | NULL | 左表有,右表无匹配(dept_id=50 不存在) |
使用场景
表格
| 场景 | 示例 |
|---|---|
| 查询所有员工,不管有没有部门 | SELECT * FROM employees LEFT JOIN departments |
| 查询未分配部门的员工 | WHERE d.dept_id IS NULL |
| 查询所有订单及客户信息(含未下单客户) | 客户表 LEFT JOIN 订单表 |
五、RIGHT JOIN(右外连接)
语法
sql
SELECT e.emp_name, d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.dept_id;
规则
表格
| 规则 | 说明 |
|---|---|
| 保留右表全部行 | departments 表所有 3 条记录都出现 |
| 左表匹配不上 | 补 NULL |
结果
表格
| emp_name | dept_name | 来源分析 |
|---|---|---|
| 张三 | 技术部 | 右表 + 左表匹配 |
| 王五 | 技术部 | 右表 + 左表匹配 |
| 李四 | 销售部 | 右表 + 左表匹配 |
| NULL | 财务部 | 右表有,左表无匹配(无员工) |
使用场景
表格
| 场景 | 示例 |
|---|---|
| 查询所有部门,不管有没有员工 | RIGHT JOIN 或 LEFT JOIN 换表顺序 |
| 查询没有员工的部门 | WHERE e.emp_id IS NULL |
注意:RIGHT JOIN 实际开发中较少使用,通常用 LEFT JOIN + 换表顺序替代,更易读。
六、FULL JOIN(全外连接)
语法
sql
SELECT e.emp_name, d.dept_name
FROM employees e
FULL JOIN departments d ON e.dept_id = d.dept_id;
规则
表格
| 规则 | 说明 |
|---|---|
| 保留两表全部行 | 左表 + 右表所有记录都出现 |
| 匹配不上的 | 各自补 NULL |
| 结果集 | LEFT JOIN 结果 ∪ RIGHT JOIN 结果 |
结果
表格
| emp_name | dept_name | 来源分析 |
|---|---|---|
| 张三 | 技术部 | 两表匹配 |
| 王五 | 技术部 | 两表匹配 |
| 李四 | 销售部 | 两表匹配 |
| 赵六 | NULL | 仅左表有 |
| 孙七 | NULL | 仅左表有 |
| NULL | 财务部 | 仅右表有 |
MySQL 特殊说明
表格
| 数据库 | FULL JOIN 支持 |
|---|---|
| PostgreSQL | ✅ 原生支持 |
| SQL Server | ✅ 原生支持 |
| Oracle | ✅ 原生支持 |
| MySQL | ❌ 不支持 |
MySQL 实现 FULL JOIN 的替代方案:
sql
-- UNION 合并 LEFT + RIGHT
SELECT e.emp_name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id
UNION
SELECT e.emp_name, d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.dept_id;
七、四种连接对比总表
表格
| 连接类型 | 保留左表全部 | 保留右表全部 | 结果行数 | 图示 |
|---|---|---|---|---|
| INNER JOIN | ❌ | ❌ | 3行(仅匹配) | 两圆交集 |
| LEFT JOIN | ✅ | ❌ | 5行(左表全部) | 左圆全部 |
| RIGHT JOIN | ❌ | ✅ | 4行(右表全部) | 右圆全部 |
| FULL JOIN | ✅ | ✅ | 6行(两表全部) | 两圆全部 |
八、未匹配数据筛选(经典场景)
场景1:查询没有部门的员工
sql
SELECT e.emp_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id
WHERE d.dept_id IS NULL;
结果:
表格
| emp_name |
|---|
| 赵六 |
| 孙七 |
场景2:查询没有员工的部门
sql
SELECT d.dept_name
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.dept_id
WHERE e.emp_id IS NULL;
-- 或更常见的写法(LEFT JOIN 换顺序)
SELECT d.dept_name
FROM departments d
LEFT JOIN employees e ON d.dept_id = e.dept_id
WHERE e.emp_id IS NULL;
结果:
表格
| dept_name |
|---|
| 财务部 |
场景3:查询两表完全无关联的数据(MySQL 模拟 FULL JOIN 取差集)
sql
-- 没有部门的员工 + 没有员工的部门
SELECT e.emp_name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id
WHERE d.dept_id IS NULL
UNION
SELECT e.emp_name, d.dept_name
FROM departments d
LEFT JOIN employees e ON d.dept_id = e.dept_id
WHERE e.emp_id IS NULL;
结果:
表格
| emp_name | dept_name |
|---|---|
| 赵六 | NULL |
| 孙七 | NULL |
| NULL | 财务部 |
九、实际开发选择指南
表格
| 需求 | 应使用的连接 |
|---|---|
| 只查两表都有的数据 | INNER JOIN |
| 查 A 表全部 + B 表匹配数据 | A LEFT JOIN B |
| 查 B 表全部 + A 表匹配数据 | B LEFT JOIN A(推荐)或 A RIGHT JOIN B |
| 查 A 表独有的数据 | A LEFT JOIN B WHERE B.key IS NULL |
| 查 B 表独有的数据 | B LEFT JOIN A WHERE A.key IS NULL |
| 查两表全部(含未匹配) | FULL JOIN(MySQL 用 UNION 模拟) |
| 查两表各自独有的数据 | (A LEFT JOIN B WHERE B IS NULL) UNION (B LEFT JOIN A WHERE A IS NULL) |
十、一句话总结
表格
| 连接 | 口诀 |
|---|---|
| INNER JOIN | 两表都有才保留 |
| LEFT JOIN | 左表全留,右表没有补 NULL |
| RIGHT JOIN | 右表全留,左表没有补 NULL |
| FULL JOIN | 两表全留,各自没有补 NULL |