Skip to content

SQL 四种表连接完全指南

更新: 7/25/2026 字数: 0 字 时长: 0 分钟

一次讲透 内连接 / 左连接 / 右连接 / 全外连接:用统一示例数据、精准语法、真实场景,配合可交互的动画,把「表与表如何匹配」这件事彻底讲清楚。

SQL 表连接原理总览

为什么需要 JOIN

关系型数据库把数据拆分到多张表中以减少冗余(范式化)。当我们需要「跨表」获取完整信息时,就要用 JOIN 把它们按某个关联字段重新拼装起来。

四种连接的本质区别只有一句话:当左右两表在关联字段上匹配不上时,这条记录要不要保留、缺失的一侧用什么填充。

  • INNER JOIN:只保留两边都匹配上的。
  • LEFT JOIN:保留左表全部,右表缺失补 NULL
  • RIGHT JOIN:保留右表全部,左表缺失补 NULL
  • FULL OUTER JOIN:两边全部保留,缺失一侧补 NULL

统一示例数据

后续所有示例都基于下面这两张表:users(用户表)与 orders(订单表),通过 users.id = orders.user_id 关联。

sql
-- 用户表
CREATE TABLE users (
  id   INT PRIMARY KEY,
  name VARCHAR(20)
);

-- 订单表
CREATE TABLE orders (
  order_id INT PRIMARY KEY,
  user_id  INT,          -- 关联 users.id,可能为空
  amount   DECIMAL(10,2)
);

INSERT INTO users (id, name) VALUES
  (1, '张三'),
  (2, '李四'),
  (3, '王五'),
  (4, '赵六');   -- 赵六尚未下过单

INSERT INTO orders (order_id, user_id, amount) VALUES
  (101, 1, 200.00),
  (102, 1, 150.00),
  (103, 2, 300.00),
  (104, 5, 500.00);  -- user_id=5 是脏数据/游客单,users 表中不存在

users 表

idname
1张三
2李四
3王五
4赵六

orders 表

order_iduser_idamount
1011200.00
1021150.00
1032300.00
1045500.00

记住这两条「边界数据」

  • 赵六(id=4):有用户,但没有订单 → 用来考验「左表独有行」。
  • 订单 104(user_id=5):有订单,但没有对应用户 → 用来考验「右表独有行」。

正是这两行,决定了四种 JOIN 结果的差异。

动画演示

SQL 四种连接可视化演示

通过用户表与订单表,直观理解 INNER / LEFT / RIGHT / FULL OUTER JOIN 的执行流程与结果差异。

① 示例数据

users(用户表)
idname
1Alice
2Bob
3Carol
4David

注:David(id=4) 没有任何订单。

orders(订单表)
order_iduser_idamount
1011200
1021350
1032120
1043500
1055800

注:订单 #105(user_id=5) 是一条孤儿订单,无对应用户。

② 选择连接类型

③ 内连接 · INNER JOIN

定义

只返回两张表中连接字段能够相互匹配的行。任意一方没有匹配的记录都会被丢弃,因此结果是两表的“交集”。

对应 SQL 语句
SELECT u.id, u.name, o.order_id, o.amount
FROM   users u
INNER JOIN orders o
  ON u.id = o.user_id;
执行流程(逐行匹配)
0 / 5

点击「下一步」或「全部展开」查看逐行匹配过程。

最终结果集 共 4 行

完成上方执行流程后,将在此展示最终结果集。

一、内连接 INNER JOIN

定义

只返回两张表在关联字段上都能匹配上的行。任意一侧匹配不上的记录都会被丢弃,是最常用、也最「严格」的连接。

内连接 INNER JOIN 韦恩图

语法

sql
SELECT 字段列表
FROM   左表 A
INNER JOIN 右表 B
  ON A.关联字段 = B.关联字段;
-- INNER 关键字可省略,JOIN 默认即内连接

示例 SQL

sql
SELECT u.id, u.name, o.order_id, o.amount
FROM   users u
INNER JOIN orders o
  ON u.id = o.user_id;

结果(赵六无订单被丢弃,订单 104 无用户也被丢弃):

idnameorder_idamount
1张三101200.00
1张三102150.00
2李四103300.00

适用场景

  • 只关心「有明确关联关系」的数据,如查询已下单用户及其订单
  • 统计报表中需要排除孤立数据的场景。
  • 多表串联查询(A→B→C 都必须存在)。

动画方案:Mermaid 分步匹配图

下图逐步展示匹配过程——高亮的连线即最终保留的结果:

二、左连接 LEFT JOIN

定义

左表为主:返回左表的全部行;对每一行,若右表有匹配则拼接,若没有则右表字段全部填 NULL。左表数据「一行都不会少」。

左连接 LEFT JOIN 韦恩图

语法

sql
SELECT 字段列表
FROM   左表 A
LEFT JOIN 右表 B      -- 亦可写作 LEFT OUTER JOIN
  ON A.关联字段 = B.关联字段;

示例 SQL

sql
SELECT u.id, u.name, o.order_id, o.amount
FROM   users u
LEFT JOIN orders o
  ON u.id = o.user_id;

结果(赵六被保留,订单字段为 NULL;订单 104 因不属于任何用户被丢弃):

idnameorder_idamount
1张三101200.00
1张三102150.00
2李四103300.00
3王五NULLNULL
4赵六NULLNULL

适用场景

  • 查询所有用户及其订单情况,包括「从未下单」的用户。
  • 找出缺失关联的数据:... LEFT JOIN ... WHERE B.key IS NULL,即可筛出「未下单用户」。
  • 主表数据必须完整展示的一切报表(如用户列表页附带统计)。

动画方案:纯 CSS 分步高亮

左表整体以蓝色高亮表示「全部保留」,无匹配行显示 NULL 徽标:

users(左表 · 全部保留)
1 张三 → 101 / 102
2 李四 → 103
3 王五 → NULL
4 赵六 → NULL
orders(右表 · 仅匹配部分参与)
101 · user 1
103 · user 2
104 · user 5(无对应用户,丢弃)

动画原理

每一行用 animation-delay 递增,形成「逐行落位」的分步效果;左列外描边(keep-all)强调「左表整体保留」,null-row 的虚线框直观表达「补 NULL」。刷新页面即可重新播放。

三、右连接 RIGHT JOIN

定义

右表为主:返回右表的全部行;左表无匹配时左侧字段填 NULL。它与 LEFT JOIN 完全对称——A RIGHT JOIN B 等价于 B LEFT JOIN A

右连接 RIGHT JOIN 韦恩图

语法

sql
SELECT 字段列表
FROM   左表 A
RIGHT JOIN 右表 B     -- 亦可写作 RIGHT OUTER JOIN
  ON A.关联字段 = B.关联字段;

示例 SQL

sql
SELECT u.id, u.name, o.order_id, o.amount
FROM   users u
RIGHT JOIN orders o
  ON u.id = o.user_id;

结果(全部订单保留,订单 104 无对应用户,用户字段为 NULL;赵六无订单被丢弃):

idnameorder_idamount
1张三101200.00
1张三102150.00
2李四103300.00
NULLNULL104500.00

适用场景

  • 明细表 / 事实表为主,检查是否存在「找不到主数据」的脏数据(如订单 104)。
  • 实践中较少直接使用,通常改写为 LEFT JOIN 以提升可读性——把要保留的表放在 FROM即可。

动画方案:Mermaid 时序图(强调「右表驱动」)

用时序图表达「以右表每一行为起点,反查左表」的驱动方向:

四、全外连接 FULL OUTER JOIN

定义

两表全部行都保留:匹配上的正常拼接,左表独有行右侧补 NULL,右表独有行左侧补 NULL。相当于 LEFT JOINRIGHT JOIN 结果的并集。

全外连接 FULL OUTER JOIN 韦恩图

语法

sql
SELECT 字段列表
FROM   左表 A
FULL OUTER JOIN 右表 B
  ON A.关联字段 = B.关联字段;

兼容性提醒

MySQL 不支持 FULL OUTER JOIN(PostgreSQL、SQL Server、Oracle 支持)。在 MySQL 中需用 LEFT JOIN + UNION + RIGHT JOIN 模拟,见下方示例。

示例 SQL

sql
-- 标准写法(PostgreSQL / SQL Server / Oracle)
SELECT u.id, u.name, o.order_id, o.amount
FROM   users u
FULL OUTER JOIN orders o
  ON u.id = o.user_id;

-- MySQL 模拟写法
SELECT u.id, u.name, o.order_id, o.amount
FROM users u LEFT JOIN orders o ON u.id = o.user_id
UNION
SELECT u.id, u.name, o.order_id, o.amount
FROM users u RIGHT JOIN orders o ON u.id = o.user_id;

结果(赵六与订单 104 都被保留):

idnameorder_idamount
1张三101200.00
1张三102150.00
2李四103300.00
3王五NULLNULL
4赵六NULLNULL
NULLNULL104500.00

适用场景

  • 数据对账 / 差异比对:同时找出「A 有 B 没有」和「B 有 A 没有」的记录。
  • 两个数据源合并去重,且都不能丢数据的场景。
  • 数据质量巡检:一次性暴露双向的孤立数据。

动画方案:CSS 韦恩图三区高亮

用纯 CSS 绘制两个交叠圆,并让「左独有 → 交集 → 右独有」三个区域依次点亮,直观表达「全部保留」:

左独有
赵六
右独有
订单104
交集
张三·李四

三步点亮

venn-left(0.1s) → venn-right(0.5s) → venn-center(0.9s) 依次出现,mix-blend-mode:multiply 让交叠区自然加深,对应「两表全保留、交集拼接」的语义。

对比总结

维度INNER JOINLEFT JOINRIGHT JOINFULL OUTER JOIN
保留范围仅两表交集左表全部 + 交集右表全部 + 交集两表全部
左表独有行❌ 丢弃✅ 保留(右补 NULL)❌ 丢弃✅ 保留(右补 NULL)
右表独有行❌ 丢弃❌ 丢弃✅ 保留(左补 NULL)✅ 保留(左补 NULL)
结果行数(本例)3546
典型场景已关联的完整数据主表全展示、找缺失明细表全展示、查脏数据双向对账、合并去重
数据库支持全部支持全部支持全部支持⚠️ MySQL 不支持,需模拟
等价改写B RIGHT JOIN AB LEFT JOIN ALEFT ∪ RIGHT

一句话记忆法

  • 想要「交集」→ INNER
  • 想要「以谁为准、谁一行不少」→ 谁在 FROM 后就用 LEFT
  • 想要「谁都不能少」→ FULL OUTER