SQL 连接查询的三种写法分别怎么用?从内连到外连一次讲清

编程狮(w3cschool.cn) 2026-10-09 07:05:26 浏览数 (34)
反馈

你写报表或接口时,几乎一定会遇到这种需求:订单表要带上用户表的昵称,文章表要带上分类表的名称。两张表靠各自的键拼到一起,这就是 SQL 连接查询要解决的事。核心答案先给出来:取两表交集用 INNER JOIN,要保留左表全部记录、右表补空用 LEFT JOIN,同一张表自己和自己比用自连接(SELF JOIN)。这篇以 MySQL 8.0 为例,把三种连接方式的写法、结果差异和最容易踩的空值坑一次讲清。今天这篇文章,编程狮就把这块讲透。

SQL 连接查询怎么写?三种方式一次讲清

先看结论

SQL 连接查询不是只有一种写法,三种手段对应不同的“取数范围”:

你的目标 首选写法 一句话理由
只要两表都匹配的行 INNER JOIN 取交集,最常用
左表全留、右表没匹配补 NULL LEFT JOIN 保留主表完整
同一张表自己和自己比 自连接 SELF JOIN 用别名拆成两张表

三种连接方式的取舍决策

一、SQL 连接查询到底在解决什么

精确查询 WHERE id = 1 只动一张表,但真实业务里数据是按“范式”拆开的:用户一个表、订单一个表、商品又一个表。要把订单和用户拼到一行,就得靠 SQL 连接查询,用“外键”当桥梁把两张表横向拼起来。想系统学语法,可以先过一遍 SQL 教程。

它解决的正是“用一张表的主键去取另一张表的属性”的问题,是后台列表页、报表和联表统计背后的核心语法。

二、INNER JOIN:取两表交集

在 SQL 连接查询里,INNER JOIN 是最基础的一种写法,它只返回两表都满足连接条件的行,也就是“交集”。语法上 ON 后面写连接条件,比老式的逗号加 WHERE 更清晰,也利于优化器选择执行计划。速查连接语法可以翻 MySQL 速查手册。

-- 查出每个订单对应的用户昵称(只保留两表都匹配的行)
SELECT o.id AS 订单号, u.nickname AS 昵称
FROM orders o
INNER JOIN users u ON o.user_id = u.id;

上面这段把 orders 和 users 按 user_id = id 拼起来。预期结果里,每个订单都带出了对应用户的昵称;如果某个订单的 user_id 在 users 里找不到,这一行会被直接丢弃——这就是“交集”的代价。

三、LEFT JOIN:保留左表全部

LEFT JOIN 是外连接的一种(左外连接),它以左表为基准,左表的每一行都会出现在结果里;右表匹配不上的字段用 NULL 补齐。它适合“列出所有用户,并附上他们最近的订单”这类不能丢左表数据的场景。想深入看多表查询可以读 MySQL 教程。

-- 列出所有用户,以及他们的一笔订单(没有订单的显示 NULL)
SELECT u.nickname AS 昵称, o.id AS 订单号
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

关键区别在这里:即使某用户从没下过单,u.nickname 依然会出现在结果中,o.id 是 NULL。正因如此,LEFT JOIN 的结果行数一定不少于左表行数,而 INNER JOIN 可能更少。

⚠️ 注意:在 LEFT JOIN 的 WHERE 里直接过滤右表字段(如 WHERE o.id IS NOT NULL)会悄悄把它变回 INNER JOIN。要保留 NULL 行,过滤条件必须写到 ON 里。

四、自连接与三种方式横向对比

自连接(SELF JOIN)是“同一张表连自己”,靠起两个不同的别名把它当成两张表来用,常用来查层级或同表对比,比如“找出工资比自己经理高的员工”。在 SQL 连接查询里这一类最容易被忽略,却非常实用。

-- 员工表连自己:找出工资高于其经理的员工
SELECT e.name AS 员工, m.name AS 经理, e.salary AS 工资
FROM employees e
INNER JOIN employees m ON e.manager_id = m.id
WHERE e.salary > m.salary;

INNER JOIN 与 LEFT JOIN 结果差异

把三种方式摆在一起对比:

方式 取数范围 右表无匹配 典型场景
INNER JOIN 两表交集 该行丢弃 订单带用户
LEFT JOIN 左表全 + 右表匹配 右表补 NULL 用户带订单
自连接 同表拆两表 视连接类型 上下级对比

踩坑清单

  1. 现象:LEFT JOIN 后行数没变少,过滤却失效;原因:把右表过滤写进了 WHERE;修复:改到 ON 子句。
  2. 现象:连出大量重复行;原因:连接键不唯一,产生笛卡尔式膨胀;修复:确认 ON 用的键是唯一的,或先去重。
  3. 现象:NULL 参与计算结果为 NULL;原因:LEFT JOIN 补的 NULL 进了 SUM/+;修复:用 COALESCE(列, 0) 兜底。
  4. 现象:自连接查不出数据;原因:两别名指向同一张表却没起不同别名;修复:必须给同一表起两个不同别名。

总结

SQL 连接查询的选型逻辑是“先想清楚要保留哪边”:两表都要就 INNER JOIN,左表不能丢就 LEFT JOIN,自己和自己比就上自连接。掌握 SQL 连接查询的这三种写法,你就能应付绝大多数联表场景。记住最痛的一课——LEFT JOIN 的 NULL 过滤必须写在 ON 里,否则会悄悄退化成内连接,把左表该保留的行删掉。

要点带走:

  • INNER JOIN 取交集,结果可能比任一表都少;
  • LEFT JOIN 保左表,右表补 NULL,过滤条件别写错地方;
  • 自连接靠双别名,是同一张表做层级对比的利器。

下一步可以动手给常用联表补上合适的索引,直观感受连接耗时的变化。

动手验证清单

下面这组动作帮你在本机验证三种连接方式,按你自己的环境执行,结果以实际输出为准(本文 SQL 未在作者本机执行,仅给出预期形态):

  1. 安装:本地起一个 MySQL 8.0 或用 Docker 拉取镜像;
  2. 配置:建 users 和 orders 两张表并插入若干测试数据,故意留一条无订单的用户;
  3. 命令:执行上面的 INNER JOIN 与 LEFT JOIN 语句;
  4. 运行:在命令行或客户端跑语句,观察返回行;
  5. 检查:对比两次结果的行数与 NULL 分布;
  6. 预期:INNER JOIN 不含无订单用户,LEFT JOIN 包含且订单号为 NULL;
  7. 失败:若报未知列,检查 ON 里的字段名与别名;
  8. 修复:统一别名写法,或显式写表名前缀;
  9. 验证:自连接语句应返回工资高于经理的员工名单。

延伸学习

想把 SQL 联表能力补完整,可以顺着这条线:

  1. 先过一遍 SQL 入门课程,把基础语法铺平;
  2. 想深入联表优化,读这两篇笔记:

常见问题

Q:INNER JOIN 和逗号加 WHERE 有什么区别?

A:结果通常一样,但 INNER JOIN 把连接条件写在 ON 里、过滤写在 WHERE 里,语义更清楚,也更容易被优化器正确识别,可读性远好于隐式逗号连接。

Q:为什么 LEFT JOIN 之后行数反而变多了?

A:多半是连接键不唯一,比如一张用户对应多条订单,左表一行就会被“复制”成多行。先确认连接键的唯一性,或按需先聚合再连。

Q:自连接一定要起两个别名吗?

A:是的。同一个表在一条语句里出现两次,必须给每次出现起不同的别名,否则数据库无法区分左右两边,连接条件也无从写起。

0 人点赞