文章目录
- 第一章 连接查询的本质:笛卡尔积与关联条件
- 第二章 内连接(INNER JOIN):精准匹配的交集
- 2.1 标准语法结构
- 2.2 经典案例实战:查询 SMITH 的部门名称
- 第三章 外连接(OUTER JOIN):主表保全的关联
- 3.1 左外连接(LEFT JOIN)
- 3.2 右外连接(RIGHT JOIN)
- 第四章 外连接实战演练:学生成绩与部门员工案例
- 4.1 案例一:左连接 —— 展示所有学生及其成绩
- 4.2 案例二:右连接 —— 展示所有成绩及其对应学生
- 4.3 综合实战:左右连接互换(列出没有员工的部门)
第一章 连接查询的本质:笛卡尔积与关联条件
在深入探讨内连接和外连接的具体语法之前,我们有必要先理解连接查询的底层数学逻辑。无论是内连接还是外连接,其第一步在数据库内部都是基于两个表做笛卡尔积(Cartesian Product)。
笛卡尔积是指将左表的每一行与右表的每一行进行组合。假设表 A 有 n 行,表 B 有 m 行,那么笛卡尔积的结果将产生 n×m 行临时数据。在实际业务中,这通常是海量且毫无意义的数据。因此,连接查询的核心操作就是在笛卡尔积的基础上,通过关联条件(通常是主外键关系)筛选出符合业务逻辑的有效数据。ON子句和WHERE子句正是我们用来裁剪这张巨大临时表的工具。理解了这一点,你就能明白为什么无条件的多表查询(即不带 WHERE 的逗号连接)会让数据库陷入巨大的性能灾难。
第二章 内连接(INNER JOIN):精准匹配的交集
内连接是日常开发中最常用的连接查询,它在逻辑上可以理解为:仅返回左表和右表中满足连接条件的交集部分。如果某一行在另一张表中没有匹配项,则该行不会出现在最终结果集中。
2.1 标准语法结构
内连接有两种主流的写法,即隐式语法(使用逗号和 WHERE)和显式语法(使用INNER JOIN和ON)。从可读性和现代数据库规范出发,我更推荐使用显式语法,因为它将表与表之间的“连接逻辑”和结果集的“过滤逻辑”分开了。
-- 隐式内连接(传统写法,易读性较差,条件一多容易混乱)SELECT字段列表FROM表1,表2WHERE连接条件AND过滤条件;-- 显式内连接(标准写法,结构清晰)SELECT字段列表FROM表1INNERJOIN表2ON连接条件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。
方法一:隐式内连接(旧式写法)
SELECTe.ename,d.dnameFROMEMP e,DEPT dWHEREe.deptno=d.deptnoANDe.ename='SMITH';方法二:显式内连接(标准写法)
SELECTe.ename,d.dnameFROMEMP eINNERJOINDEPT dONe.deptno=d.deptnoWHEREe.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填充。
语法结构:
SELECT字段列表FROM表1-- 主表(完全显示)LEFTJOIN表2-- 从表(匹配不上则显示 NULL)ON表1.关联字段=表2.关联字段;适用场景:最常见于“主从数据”查询,例如“查询所有用户及其订单信息,无订单的用户也要显示”。
3.2 右外连接(RIGHT JOIN)
定义:与左外连接恰好相反。RIGHT JOIN右侧的表(即右表)中的所有记录都会被完整保留到结果集中。如果左表没有匹配,则左表字段以NULL填充。
语法结构:
SELECT字段列表FROM表1-- 从表(匹配不上则显示 NULL)RIGHTJOIN表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)。
-- 创建学生表CREATETABLEstu(idINT,nameVARCHAR(30));INSERTINTOstuVALUES(1,'jack'),(2,'tom'),(3,'kity'),(4,'nono');-- 创建成绩表CREATETABLEexam(idINT,gradeINT);INSERTINTOexamVALUES(1,56),(2,76),(11,8);业务需求:查询所有学生的成绩,如果学生没有成绩,也要显示该学生的个人信息。
分析:需求要求保留所有学生,因此学生表stu必须是主表(左表)。
SELECTs.id,s.name,e.gradeFROMstu sLEFTJOINexam eONs.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必须是主表(右表)。
SELECTs.id,s.name,e.idASexam_id,e.gradeFROMstu sRIGHTJOINexam eONs.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 作为左表)
SELECTd.dname,e.*FROMdept dLEFTJOINemp eONd.deptno=e.deptno;这张 SQL 以部门为主视角,无论部门下是否有员工,部门名称都会出现。如果该部门下无员工,则员工e.*的所有字段均为NULL。
方案二:使用右外连接(DEPT 作为右表)
SELECTd.dname,e.*FROMemp eRIGHTJOINdept dONd.deptno=e.deptno;这张 SQL 将DEPT放在了RIGHT JOIN的右侧,将其变为主表。执行结果与方案一完全一致。
开发建议:在实际编码中,你完全可以根据业务主语的表述顺序,灵活选用LEFT或RIGHT。但出于代码的一致性考虑,我强烈建议团队统一规范,只使用LEFT JOIN,将主表固定放在左边,这样可以极大减少因表顺序混乱导致的逻辑误判。