在实际开发中,80% 的查询需求都涉及多表关联(如 “查询员工及其部门”“显示所有学生成绩,含无成绩的学生”)。MySQL 的表连接分为 “内连接” 和 “外连接”,内连接仅保留匹配数据,外连接则能保留某一张表的所有数据(即使无匹配),两者适用场景完全不同。本文以 “学生成绩查询”“部门员工关联” 为实战案例,系统讲解内连接、左外连接、右外连接的语法、差异及应用场景,帮你精准选择连接方式,避免数据遗漏或冗余。
一、表连接基础:从笛卡尔积到连接条件
在学习具体连接方式前,需先理解 “表连接的本质”—— 多表查询的核心是通过 “连接条件” 过滤无效的 “笛卡尔积”(多表未过滤时的全量组合),确保结果精准。
1.1 笛卡尔积(需避免)
当多表查询未加连接条件时,会产生 “笛卡尔积”—— 表 1 的每一行与表 2 的每一行组合,数据量为 “表 1 行数 × 表 2 行数”,通常是无效数据。示例:学生表(4 行)与成绩表(3 行)未加连接条件,会产生4×3=12行数据,其中大部分无意义(如学生 ID=3 与成绩 ID=11 的组合)。
1.2 连接条件(核心)
通过ON或WHERE指定 “关联字段”(如学生表的id与成绩表的id),过滤笛卡尔积,仅保留匹配的数据。示例:stu.id = exam.id作为连接条件,仅保留学生 ID 与成绩 ID 一致的组合,数据量回归正常。
二、内连接(INNER JOIN):仅保留匹配数据
内连接是最常用的连接方式,核心是 “只保留两张表中连接条件匹配的记录”,不匹配的记录会被过滤(如无成绩的学生、无对应学生的成绩)。
2.1 语法:两种常见写法
写法 1:传统逗号分隔表(WHERE 加连接条件)
SELECT 表1.字段, 表2.字段
FROM 表1, 表2
WHERE 表1.关联字段 = 表2.关联字段 -- 连接条件
AND 其他筛选条件; -- 业务筛选条件
写法 2:标准 INNER JOIN(ON 加连接条件,推荐)
SELECT 表1.字段, 表2.字段
FROM 表1
INNER JOIN 表2 ON 表1.关联字段 = 表2.关联字段 -- 连接条件(必选)
WHERE 其他筛选条件; -- 业务筛选条件(可选)
INNER可省略,简写为JOIN;
连接条件用ON指定,业务筛选用WHERE指定,逻辑更清晰,推荐优先使用。
2.2 实战案例:查询 SMITH 的姓名与部门名
需求:数据来自EMP(员工表,含ename姓名、deptno部门号)和DEPT(部门表,含dname部门名、deptno部门号),仅保留 SMITH 的关联数据。
传统写法
SELECT e.ename, d.dname
FROM EMP e, DEPT d
WHERE e.deptno = d.deptno -- 连接条件:部门号匹配
AND e.ename = 'SMITH'; -- 业务筛选:仅SMITH
标准内连接写法
SELECT e.ename, d.dname
FROM EMP e
INNER JOIN DEPT d ON e.deptno = d.deptno -- 连接条件
WHERE e.ename = 'SMITH'; -- 业务筛选
输出结果:仅显示 SMITH 的姓名(SMITH)和部门名(RESEARCH),无其他无关数据。
2.3 适用场景
需精准关联两张表的匹配数据,不保留任何一方的 “无匹配记录”;
典型场景:查询 “有订单的用户”“有员工的部门”“有成绩的学生”。
三、外连接:保留某一张表的所有数据
外连接分为 “左外连接” 和 “右外连接”,核心是 “保留某一张表的所有记录,另一张表无匹配时补 NULL”,解决 “需显示‘无关联数据’的场景”(如 “显示所有学生,含无成绩的学生”“显示所有部门,含无员工的部门”)。
3.1 左外连接(LEFT JOIN):保留左表所有数据
左外连接的核心是 “左表的所有记录都会显示,右表无匹配时,对应字段补 NULL”(左表指FROM后的表,右表指LEFT JOIN后的表)。
语法
SELECT 表1.字段, 表2.字段
FROM 表1 -- 左表:所有记录都会保留
LEFT JOIN 表2 -- 右表:无匹配时补NULL
ON 表1.关联字段 = 表2.关联字段 -- 连接条件
[WHERE 其他筛选条件];
实战案例:显示所有学生的成绩(含无成绩的学生)
需求:学生表stu(4 个学生)与成绩表exam(3 条成绩,含无效学生 ID=11),需显示所有学生,无成绩的学生成绩字段补 NULL。
步骤 1:创建测试表并插入数据
-- 学生表(左表)
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); -- ID=11无对应学生
步骤 2:左外连接查询
SELECT
s.id AS 学生ID,
s.name AS 学生姓名,
e.grade AS 成绩
FROM stu s -- 左表:所有学生都会显示
LEFT JOIN exam e -- 右表:无匹配时成绩补NULL
ON s.id = e.id; -- 连接条件:学生ID=成绩ID
输出结果
| 学生 ID | 学生姓名 | 成绩 | |
|---|---|---|---|
| 1 | jack | 56 | |
| 2 | tom | 76 | |
| 3 | kity | NULL | -- 无成绩,补 NULL |
| 4 | nono | NULL | -- 无成绩,补 NULL |
关键:左表stu的 4 个学生全部显示,右表exam中 ID=11 的成绩因无对应学生,未被显示(左外连接仅保证左表完整)。
3.2 右外连接(RIGHT JOIN):保留右表所有数据
右外连接与左外连接相反,核心是 “右表的所有记录都会显示,左表无匹配时,对应字段补 NULL”(右表指RIGHT JOIN后的表)。
语法
SELECT 表1.字段, 表2.字段
FROM 表1 -- 左表:无匹配时补NULL
RIGHT JOIN 表2 -- 右表:所有记录都会保留
ON 表1.关联字段 = 表2.关联字段 -- 连接条件
[WHERE 其他筛选条件];
实战案例:显示所有成绩(含无对应学生的成绩)
需求:基于上述stu和exam表,需显示所有成绩,无对应学生的成绩,学生字段补 NULL。
右外连接查询
SELECT
s.id AS 学生ID,
s.name AS 学生姓名,
e.grade AS 成绩
FROM stu s -- 左表:无匹配时学生信息补NULL
RIGHT JOIN exam e -- 右表:所有成绩都会显示
ON s.id = e.id; -- 连接条件:学生ID=成绩ID
输出结果
| 学生 ID | 学生姓名 | 成绩 | |
|---|---|---|---|
| 1 | jack | 56 | |
| 2 | tom | 76 | |
| NULL | NULL | 8 | -- 无对应学生,补 NULL |
关键:右表exam的 3 条成绩全部显示,左表stu中无 ID=11 的学生,对应字段补 NULL。
3.3 左外连接与右外连接的转换
左外连接和右外连接可以通过 “交换表的位置” 相互转换,例如:“表 A LEFT JOIN 表 B” 等价于 “表 B RIGHT JOIN 表 A”。
示例:显示所有部门(含无员工的部门),可用两种方式实现:
-- 方式1:左外连接(部门表为左表,保留所有部门)
SELECT d.dname, e.*
FROM dept d
LEFT JOIN emp e ON d.deptno = e.deptno;
-- 方式2:右外连接(部门表为右表,保留所有部门)
SELECT d.dname, e.*
FROM emp e
RIGHT JOIN dept d ON d.deptno = e.deptno;
两种方式结果完全一致,均会显示所有部门(如无员工的 40 号部门),无员工的部门对应员工字段补 NULL。
四、内连接与外连接的核心差异
| 维度 | 内连接(INNER JOIN) | 左外连接(LEFT JOIN) | 右外连接(RIGHT JOIN) |
|---|---|---|---|
| 数据保留规则 | 仅保留两表匹配的记录 | 保留左表所有记录,右表无匹配补 NULL | 保留右表所有记录,左表无匹配补 NULL |
| 适用场景 | 需精准关联匹配数据 | 需显示左表所有数据(如 “所有学生”) | 需显示右表所有数据(如 “所有成绩”) |
| 结果数据量 | ≤ 两表行数的较小值 | = 左表行数 | = 右表行数 |
| NULL 值情况 | 无 NULL(仅匹配数据) | 右表字段可能为 NULL | 左表字段可能为 NULL |
五、实战技巧与注意事项
-
连接条件用 ON,筛选条件用 WHERE连接条件(如
stu.id = exam.id)必须放在ON后,业务筛选条件(如e.grade > 60)放在WHERE后,避免逻辑混淆。错误示例:将业务筛选放在ON后,会导致左外连接失效(如LEFT JOIN exam e ON s.id = e.id AND e.grade > 60,无成绩的学生仍会显示,但成绩字段补 NULL,逻辑正确;但筛选 “有成绩且成绩> 60” 需用WHERE)。 -
表别名简化代码多表连接时,用别名(如
stu s、exam e)简化字段引用,避免字段名重复(如id在两张表中均存在,需用s.id区分)。 -
外连接需注意 NULL 值处理外连接结果中可能包含 NULL,统计时需用
IFNULL处理(如统计学生平均成绩,无成绩的学生需视为 0 分):SELECT s.name, IFNULL(e.grade, 0) AS 成绩 -- NULL转为0 FROM stu s LEFT JOIN exam e ON s.id = e.id; -
避免过度外连接若无需保留 “无匹配数据”,优先用内连接(性能更高,结果更简洁);仅当业务明确要求保留某表所有数据时,才用外连接。
六、总结
MySQL 表连接是跨表查询的核心,选择正确的连接方式能直接影响数据完整性和查询效率:
- 内连接:精准关联匹配数据,适用于 “有关联才需要显示” 的场景(如 “有订单的用户”);
- 左外连接:保留左表所有数据,适用于 “需显示左表全部,右表可选” 的场景(如 “所有学生,含无成绩的学生”);
- 右外连接:保留右表所有数据,适用于 “需显示右表全部,左表可选” 的场景(如 “所有成绩,含无对应学生的成绩”)。

1771

被折叠的 条评论
为什么被折叠?



