9. 表的内连和外连【由浅入深-MySQL】

文章目录

  • [第一章 连接查询的本质:笛卡尔积与关联条件](#第一章 连接查询的本质:笛卡尔积与关联条件)
  • [第二章 内连接(INNER JOIN):精准匹配的交集](#第二章 内连接(INNER JOIN):精准匹配的交集)
    • [2.1 标准语法结构](#2.1 标准语法结构)
    • [2.2 经典案例实战:查询 SMITH 的部门名称](#2.2 经典案例实战:查询 SMITH 的部门名称)
  • [第三章 外连接(OUTER JOIN):主表保全的关联](#第三章 外连接(OUTER JOIN):主表保全的关联)
    • [3.1 左外连接(LEFT JOIN)](#3.1 左外连接(LEFT JOIN))
    • [3.2 右外连接(RIGHT JOIN)](#3.2 右外连接(RIGHT JOIN))
  • [第四章 外连接实战演练:学生成绩与部门员工案例](#第四章 外连接实战演练:学生成绩与部门员工案例)
    • [4.1 案例一:左连接 ------ 展示所有学生及其成绩](#4.1 案例一:左连接 —— 展示所有学生及其成绩)
    • [4.2 案例二:右连接 ------ 展示所有成绩及其对应学生](#4.2 案例二:右连接 —— 展示所有成绩及其对应学生)
    • [4.3 综合实战:左右连接互换(列出没有员工的部门)](#4.3 综合实战:左右连接互换(列出没有员工的部门))

第一章 连接查询的本质:笛卡尔积与关联条件

在深入探讨内连接和外连接的具体语法之前,我们有必要先理解连接查询的底层数学逻辑。无论是内连接还是外连接,其第一步在数据库内部都是基于两个表做笛卡尔积(Cartesian Product)。

笛卡尔积是指将左表的每一行与右表的每一行进行组合。假设表 A 有 n 行,表 B 有 m 行,那么笛卡尔积的结果将产生 n×m 行临时数据。在实际业务中,这通常是海量且毫无意义的数据。因此,连接查询的核心操作就是在笛卡尔积的基础上,通过关联条件 (通常是主外键关系)筛选出符合业务逻辑的有效数据。ON 子句和 WHERE 子句正是我们用来裁剪这张巨大临时表的工具。理解了这一点,你就能明白为什么无条件的多表查询(即不带 WHERE 的逗号连接)会让数据库陷入巨大的性能灾难。

第二章 内连接(INNER JOIN):精准匹配的交集

内连接是日常开发中最常用的连接查询,它在逻辑上可以理解为:仅返回左表和右表中满足连接条件的交集部分。如果某一行在另一张表中没有匹配项,则该行不会出现在最终结果集中。

2.1 标准语法结构

内连接有两种主流的写法,即隐式语法 (使用逗号和 WHERE)和显式语法 (使用 INNER JOIN 和 ON)。从可读性和现代数据库规范出发,我更推荐使用显式语法,因为它将表与表之间的"连接逻辑"和结果集的"过滤逻辑"分开了。

sql 复制代码
-- 隐式内连接(传统写法,易读性较差,条件一多容易混乱)
SELECT 字段列表
FROM 表1, 表2
WHERE 连接条件 AND 过滤条件;

-- 显式内连接(标准写法,结构清晰)
SELECT 字段列表
FROM 表1
INNER JOIN 表2 ON 连接条件
WHERE 过滤条件;

各子句拆解:

  • INNER JOIN:声明连接类型为内连接,INNER 关键字通常可以省略,直接写作 JOIN。
  • ON 子句:专门用于指定两张表之间的关联关系 (例如:EMP.deptno = DEPT.deptno)。这是连接查询的灵魂,决定了笛卡尔积的裁剪逻辑。
  • WHERE 子句:用于对连接后生成的临时结果集进行业务维度的二次筛选 (例如:ename='SMITH')。

常见误区 :很多初学者会将关联条件写在 WHERE 中,虽然结果正确,但在复杂查询中会将连接逻辑与业务逻辑混为一谈,极大地降低了 SQL 的可维护性。

2.2 经典案例实战:查询 SMITH 的部门名称

我们以经典的 EMP(员工表)和 DEPT(部门表)为例,演示两种语法的实现。假设 EMP 表中有 ename 和 deptno,DEPT 表中有 deptno 和 dname。

方法一:隐式内连接(旧式写法)

sql 复制代码
SELECT e.ename, d.dname
FROM EMP e, DEPT d
WHERE e.deptno = d.deptno AND e.ename = 'SMITH';

方法二:显式内连接(标准写法)

sql 复制代码
SELECT e.ename, d.dname
FROM EMP e
INNER JOIN DEPT d ON e.deptno = d.deptno
WHERE e.ename = 'SMITH';

执行逻辑推演 :

数据库首先计算 EMP 和 DEPT 的笛卡尔积。接着,ON e.deptno = d.deptno 将结果集限制为只有员工所在部门编号与部门表编号匹配的记录。最后,WHERE e.ename = 'SMITH' 在连接后的结果中精准定位到 SMITH 这条记录,并投影出姓名和部门名称。如果 SMITH 的 deptno 在 DEPT 表中不存在,那么内连接将返回空结果。

第三章 外连接(OUTER JOIN):主表保全的关联

与内连接只取交集不同,外连接会强制保留某一边(或两边)的所有数据,即使它们在另一张表中没有匹配项。 MySQL 支持两种外连接:左外连接(LEFT JOIN)和右外连接(RIGHT JOIN)。

3.1 左外连接(LEFT JOIN)

定义:当进行联合查询时,LEFT JOIN 左侧的表(即左表)中的所有记录都会被完整保留到结果集中。如果右表中存在满足 ON 条件的匹配行,则显示右表数据;如果右表中没有匹配行,则右表所有字段以 NULL 填充。

语法结构:

sql 复制代码
SELECT 字段列表
FROM 表1 -- 主表(完全显示)
LEFT JOIN 表2 -- 从表(匹配不上则显示 NULL)
ON 表1.关联字段 = 表2.关联字段;

适用场景:最常见于"主从数据"查询,例如"查询所有用户及其订单信息,无订单的用户也要显示"。

3.2 右外连接(RIGHT JOIN)

定义:与左外连接恰好相反。RIGHT JOIN 右侧的表(即右表)中的所有记录都会被完整保留到结果集中。如果左表没有匹配,则左表字段以 NULL 填充。

语法结构:

sql 复制代码
SELECT 字段列表
FROM 表1 -- 从表(匹配不上则显示 NULL)
RIGHT JOIN 表2 -- 主表(完全显示)
ON 表1.关联字段 = 表2.关联字段;

重要心法 :LEFT JOIN 和 RIGHT JOIN 在功能上是完全等价的,本质上互为语法糖。只要将 LEFT JOIN 中左右两表的顺序互换,同时将 LEFT 改为 RIGHT,查询结果将完全一致。因此,在实际开发中,LEFT JOIN 的使用频率远高于 RIGHT JOIN,因为人们更习惯将主表写在前面。

第四章 外连接实战演练:学生成绩与部门员工案例

为了深入理解外连接的特性,我们通过两组具体的案例来验证 NULL 填充的细节和左右连接的互换技巧。

4.1 案例一:左连接 ------ 展示所有学生及其成绩

准备环境 :

首先创建学生表 stu 和成绩表 exam,注意观察两者 ID 的差异(stu 表有 ID 4,exam 表有 ID 11)。

sql 复制代码
-- 创建学生表
CREATE TABLE stu (
    id INT,
    name VARCHAR(30)
);
INSERT INTO stu VALUES (1, 'jack'), (2, 'tom'), (3, 'kity'), (4, 'nono');

-- 创建成绩表
CREATE TABLE exam (
    id INT,
    grade INT
);
INSERT INTO exam VALUES (1, 56), (2, 76), (11, 8);

业务需求:查询所有学生的成绩,如果学生没有成绩,也要显示该学生的个人信息。

分析:需求要求保留所有学生,因此学生表 stu 必须是主表(左表)。

sql 复制代码
SELECT s.id, s.name, e.grade
FROM stu s
LEFT JOIN exam e ON s.id = e.id;

结果分析:

  • 学生 jack(ID=1)匹配到成绩 56,正常显示。
  • 学生 tom(ID=2)匹配到成绩 76,正常显示。
  • 学生 kity(ID=3)和 nono(ID=4)在 exam 表中无对应记录,因此 e.grade 字段返回 NULL,但学生个人信息完整显示。

4.2 案例二:右连接 ------ 展示所有成绩及其对应学生

业务需求 :对 stu 表和 exam 表联合查询,把所有的成绩都显示出来,即使这个成绩没有学生与它对应。

分析:需求要求保留所有成绩,因此成绩表 exam 必须是主表(右表)。

sql 复制代码
SELECT s.id, s.name, e.id AS exam_id, e.grade
FROM stu s
RIGHT JOIN exam e ON s.id = e.id;

结果分析:

  • 成绩 56(ID=1)和 76(ID=2)分别对应 jack 和 tom,正常显示。
  • 成绩 8(ID=11)在 stu 表中查无此人,因此左表 stu 的 s.id 和 s.name 全部返回 NULL,但成绩数据 8 被完整保留。

4.3 综合实战:左右连接互换(列出没有员工的部门)

业务需求:列出部门名称和这些部门的员工信息,同时列出没有员工的部门。

这是一个非常经典的"存在缺失关联"的查询场景。需求要求保留所有部门(即使没有员工),因此部门表 DEPT 必须是主表。我们利用左右连接等价性,提供两种完全等效的实现方案。

方案一:使用左外连接(DEPT 作为左表)

sql 复制代码
SELECT d.dname, e.*
FROM dept d
LEFT JOIN emp e ON d.deptno = e.deptno;

这张 SQL 以部门为主视角,无论部门下是否有员工,部门名称都会出现。如果该部门下无员工,则员工 e.* 的所有字段均为 NULL。

方案二:使用右外连接(DEPT 作为右表)

sql 复制代码
SELECT d.dname, e.*
FROM emp e
RIGHT JOIN dept d ON d.deptno = e.deptno;

这张 SQL 将 DEPT 放在了 RIGHT JOIN 的右侧,将其变为主表。执行结果与方案一完全一致。

开发建议 :在实际编码中,你完全可以根据业务主语的表述顺序,灵活选用 LEFT 或 RIGHT。但出于代码的一致性考虑,我强烈建议团队统一规范,只使用 LEFT JOIN,将主表固定放在左边,这样可以极大减少因表顺序混乱导致的逻辑误判。

相关推荐
嘟哩DuliDuli1 小时前
剧本节点输入归一化:文本来源、版本与下游引用
android·人工智能·安全·ai·软件工程
Techblog of HaoWANG1 小时前
Ubuntu 下将 Ollama 服务配置为监听 0.0.0.0:11434 完整实践
linux·数据库·ubuntu·ollama
小诗懂技术1 小时前
V4L2视频采集流程复习笔记
笔记·mysql·音视频
似水流年QC1 小时前
LangChain 入门与实战
服务器·数据库·langchain
high一马平川1 小时前
# SystemUI Dagger2 精读 · 第 2 章 @Inject:1200 处构造注入,和 1 处例外
android·java·kotlin
周厉1 小时前
酒店PMS系统设计:房型房价管理与动态价格优先级实现(Java/MySQL)
java·数据库·pms酒店系统
howard20051 小时前
任务 2-3 图形化管理工具Navicat
数据库·mysql·navicat
一碗白开水一2 小时前
Day 5 · 弹性 | 微观
java·前端·数据库