MySQL数据库 (十一) MySQL表的内外连接,内连接,左外连接,右外连接

目录

一、内连接

什么是内连接?

语法格式:

二、外连接

左外连接

什么是左外连接?

语法:

右外连接

什么是右外连接?

语法:

三、总结


表的连接分为内连接和外连接,而内连接是使用最频繁的连接方式,也是我们之前学习过的所有多表查询的底层逻辑。

一、内连接

什么是内连接?

内连接的本质就是对两张表生成的笛卡尔积结果,通过条件筛选出有效记录,我们前面写的所有多表查询,比如员工表和部门表的关联查询,其实都属于内连接的范畴。它的核心逻辑是,只有两张表中满足连接条件的记录,才会被保留在最终结果中,不匹配的记录会被直接过滤掉,就像我们之前用where emp.deptno = dept.deptno筛选员工和部门的关联数据一样,只有部门号匹配的记录才会被保留。

语法格式:

在语法上,内连接有专门的 inner join 写法,格式为 select 字段 from 表1 inner join 表2 on 连接条件 and 其他条件,其中 on 关键字用来指定两张表的连接条件,and 后面可以追加额外的业务筛选条件。这种写法和我们之前用 where 条件实现的内连接是等价的,只是语法结构更清晰,把连接条件和筛选条件分开了,也更符合现代 SQL 的书写规范。

需要注意的是,我们之前学习的笛卡尔积也属于内连接的一种特殊情况,当不写任何 on 或 where 条件时,两张表会直接生成完整的笛卡尔积,此时的连接条件相当于恒成立,所有记录都会被保留。而我们实际使用的内连接,都是在笛卡尔积的基础上,通过条件过滤出有业务意义的记录,这也是内连接最核心的作用。

示例: 显示SMITH的名字和部门名称

还是这两张表:emp 员工表和 dept 部门表,员工表中没有部门名称,部门表中没有员工信息,要同时获取这两个字段,就必须通过两张表的关联查询来实现。

我们之前的写法:

之前的写法就是用 where 条件实现的内连接。执行 select * from emp, dept where emp.deptno=dept.deptno; 时,数据库会先对两张表做笛卡尔积,再通过 where 条件筛选出部门号匹配的记录,最终得到 14 条数据,每条记录都同时包含员工信息和对应的部门信息。

因为我们只需要员工姓名和部门名称,可以把语句改成 select emp.ename, dept.dname from emp, dept where emp.deptno=dept.deptno;,结果中就会只显示员工姓名和对应的部门名称,比如 SMITH 对应 RESEARCH,ALLEN 对应 SALES,这和我们的需求匹配。

现在我们可以换种写法:

我们切换到 inner join 的标准写法,执行 select * fromemp inner join depton emp.deptno=dept.deptno;。和之前的写法一样,数据库会先做笛卡尔积,再通过 on 子句的连接条件筛选有效记录,结果和 where 条件写法完全一致,同样是 14 条包含员工和部门信息的记录。

因为我们只需要目标字段,所以执行 select ename, dname from emp inner join dept on emp.deptno=dept.deptno;,结果也和之前的字段筛选结果完全相同,说明两种写法在功能上是等价的,只是语法结构不同。

我们也可以在内连接中添加额外筛选条件。第一种是在 on 子句后用 and 追加条件,执行 select ename,dname from emp inner join dept on emp.deptno=dept.deptno and ename='SMITH';,结果只会返回 SMITH 的姓名和部门名称;第二种是在 inner join 语句后用 where 子句追加条件,执行 select ename,dname from emp inner join dept on emp.deptno=dept.deptno where ename='SMITH';,结果和第一种方式一致。这说明在内连接中,and 和 where 都可以用来添加业务筛选条件,只是 on 子句主要用于指定连接条件,而 where 子句更适合添加后续的过滤条件,实际使用中可以根据习惯选择,两种方式的执行结果没有区别。

二、外连接

我们现在来看外连接 ,它和内连接的核心区别就是会保留不满足连接条件的记录,而不是直接过滤掉。外连接主要分为左外连接和右外连接两种,这里我们先从左外连接说起。

左外连接

什么是左外连接?

左外连接的核心定义是 : 在联合查询中,左侧的表会被完全显示,也就是左侧表的所有记录都会被保留下来,不管它在右侧表中有没有匹配的记录。如果右侧表中存在匹配的记录,就会正常关联显示;如果没有匹配的记录,右侧表对应的字段会显示为 NULL,但左侧表的记录依然会被保留。

语法:

在语法上,左外连接的格式是 select 字段名 from 表1 left join 表2 on 连接条件,其中 表1 就是我们所说的 "左侧表",它的所有记录都会被保留,而 表2 是右侧表,只保留和左侧表匹配的记录。这种写法的关键在于 left join 关键字,它明确指定了以左侧表为基准进行连接,这也是它和内连接最本质的区别 ------ 内连接只保留两张表都匹配的记录,而左外连接会优先保留左侧表的全部数据,再去匹配右侧表的数据。

我们重新建两张表: 学生表和成绩表

这两张表分别是学生表 stu 和成绩表 exam,它们通过 id 字段建立关联关系。创建学生表时,我们用 create table stu(id int, name varchar(30)); 定义了表结构,包含学生编号 id 和姓名 name 两个字段,随后插入了 4 条学生数据,执行 select * from stu; 后可以看到,表中共有 4 名学生,编号分别为 1、2、3、4。接着创建成绩表 exam,使用 create table exam(id int, grade int); 定义了学生编号 id 和成绩 grade 字段并插入数据,执行 select * from exam; 后能看到,成绩表中只有编号 1、2 的学生有成绩记录,还有一条编号 11 的无效成绩数据,和学生表的数据并不完全匹配,这种数据差异正是用来演示内连接和左外连接区别的关键。

示例: 查询所有学生的成绩,如果这个学生没有成绩,也要将学生的个人信息显示出来

如果是内连接的话:

执行 select * from stu inner join exam on stu.id=exam.id;,内连接只会保留两张表中 id 字段匹配的记录,所以只有编号 1 的 jack 和编号 2 的 tom 被查询出来,共 2 条结果。编号 3 和 4 的学生因为在成绩表中没有匹配的记录,被直接过滤掉了,这显然不符合 "查询所有学生成绩,无成绩也要显示学生信息" 的需求,也体现了内连接 "只保留匹配数据" 的局限性。

如果是左外连接:

执行 select * from stu left join exam on stu.id=exam.id;,这里以左侧的学生表 stu 为主表,它的所有记录都会被完整保留,再去匹配右侧成绩表 exam 中的数据。执行结果中,4 名学生全部被显示出来,编号 1 和 2 的学生因为有匹配的成绩,正常显示了对应的 grade 字段;而编号 3 的 kity 和编号 4 的 nono 因为没有匹配的成绩,exam 表对应的 id 和 grade 字段都显示为 NULL,既保留了所有学生信息,也体现了他们无成绩的状态。这也清晰展示了左外连接的核心特点:左侧主表的数据全部保留,右侧表的数据根据连接条件匹配,不匹配的字段以 NULL 填充,这也是它和内连接最本质的区别。

右外连接

我们现在来看和左外连接对应的右外连接,它的逻辑和左外连接刚好相反。

什么是右外连接?

右外连接的定义是,在联合查询中,右侧的表会被完全显示,不管它在左侧表中有没有匹配的记录。

语法:

它的语法格式是 select 字段 from 表1 right join 表2 on 连接条件,其中 表2 就是右侧的主表,它的所有记录都会被完整保留,而 表1 是左侧表,只保留和右侧表匹配的记录,不匹配的字段会显示为 NULL。
示例1 : 对 stu 表和 exam 表联合查询,把所有的成绩都显示出来,即使这个成绩没有学生与它对应,也要 显示出来
也就是以右侧表为主:

当我们执行 select * from stu right join exam on stu.id=exam.id; 时,以右侧的成绩表 exam 为主表,它的所有记录都会被保留。执行结果里,成绩表中的 3 条记录全部显示了出来:id 为 1 和 2 的成绩在学生表中有匹配的记录,正常显示了学生的 id 和 name;而 id 为 11 的成绩在学生表中没有对应的学生,所以左侧学生表的 id 和 name 字段都显示为 NULL,但这条成绩记录依然被保留了下来,实现了 "显示所有成绩,即使没有对应学生" 的需求。
我们也可以使用左外连接交换位置也能达到同样的效果,只需要交换两张表的位置。比如上面的右外连接语句,等价于 select * from exam left join stu on stu.id=exam.id;,此时以 exam 作为左侧主表,同样会保留所有成绩记录,不匹配的学生字段显示为 NULL,结果和右外连接完全一致。这也说明,在实际开发中,大多数场景用左外连接就能覆盖需求,右外连接更多是作为一种对称的写法存在,理解它的核心逻辑,就能和左外连接互相转换使用。

示例2 : 列出部门名称和这些部门的员工信息,同时列出没有员工的部门

我们继续回到 emp 和 dept 这两张表。

首先,我们先看两张表的数据情况。执行 select distinct deptno from emp; 后,可以看到员工表中只有 10、20、30 这三个部门有员工;再执行 select * from dept;,能看到部门表中共有 10、20、30、40 四个部门,其中 40 号部门 "OPERATIONS" 没有任何员工。这就出现了数据不匹配的情况,内连接会直接过滤掉没有员工的 40 号部门,而外连接的作用,就是把这类 "主表存在、关联表无匹配" 的记录也保留下来。

下面我们就进行左外连接:

接着我们用左外连接来实现需求,执行 select * from dept left join emp on dept.deptno=emp.deptno;。这里我们把 dept 部门表作为左侧主表,所以所有部门记录都会被完整保留,再去匹配右侧 emp 员工表中的数据。执行结果中,10、20、30 号部门因为有员工,正常显示了对应的员工信息;而 40 号部门因为没有员工,员工表对应的字段全部显示为 NULL,但部门本身的信息依然被保留了下来,实现了 "同时列出没有员工的部门" 的需求。

为了让结果更直观,我们可以加上 order by dept.deptno asc; 对部门号进行排序,执行 select * from dept left join emp on dept.deptno=emp.deptno order by dept.deptno asc; 后,结果会按部门号从小到大排列,40 号部门会出现在最后一行,所有员工字段都为 NULL,一眼就能看出它是没有员工的部门。这也再次验证了左外连接的核心逻辑:以左侧主表为基准,保留所有记录,右侧表匹配不到的字段用 NULL 填充,完美解决了内连接无法处理的 "主表数据全部保留" 场景。

三、总结

本文介绍了SQL中表的连接操作,重点对比了内连接和外连接的区别。内连接通过笛卡尔积加条件筛选,只保留两表匹配的记录;而外连接会保留主表所有记录,不匹配的字段显示NULL。左外连接保留左表全部数据,右外连接保留右表全部数据,两者可通过交换表位置相互转换。通过员工表、部门表、学生表等实例演示了不同连接方式的应用场景,如查询所有部门(含无员工部门)需使用左外连接。理解这些连接方式的本质差异对编写高效SQL查询至关重要。

谢谢大家的观看!