外连接退化为内连接
在外连接(LEFT JOIN / RIGHT JOIN)查询中,ON 和 WHERE 的作用有所不同。ON 子句用于指定表之间的连接条件,确定主表和从表的匹配规则,同时也可以对从表的数据进行提前过滤,即在连接时就排除不符合条件的从表记录。对于外连接而言,ON 过滤不会丢掉主表记录,未匹配的从表字段会显示为 NULL。而 WHERE 子句用于对整个连接结果集进行过滤,如果在 WHERE 中对从表字段加上条件,可能会把原本保留的主表记录过滤掉,从而破坏外连接的意义。也就是说,条件放在 WHERE 子句中不当时,有可能使外连接的效果退化为内连接。
假设有两张表:学生表 和 成绩表,以学生表作为主表、成绩表作为从表来举例说明外连接的作用。
- 学生表(主表)
| 学号 | 姓名 |
|---|---|
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
- 成绩表(从表)
| 学号 | 课程 | 分数 |
|---|---|---|
| 1 | 数学 | 90 |
| 1 | 英语 | 85 |
| 2 | 数学 | 70 |
注意:王五(学号3)没有成绩记录
1. 正确写法
Section titled “1. 正确写法”SELECT 学生表.姓名, 成绩表.课程FROM 学生表LEFT JOIN 成绩表 ON 学生表.学号 = 成绩表.学号 AND 成绩表.课程 = '数学'; -- 条件在 ON结果:
| 姓名 | 课程 |
|---|---|
| 张三 | 数学 |
| 李四 | 数学 |
| 王五 | NULL |
这才是
LEFT JOIN的本意:所有学生都要出现,没数学成绩就显示NULL。
2. 错误写法
Section titled “2. 错误写法”SELECT 学生表.姓名, 成绩表.课程FROM 学生表LEFT JOIN 成绩表 ON 学生表.学号 = 成绩表.学号WHERE 成绩表.课程 = '数学'; -- 条件在 WHERE结果:
| 姓名 | 课程 | |
|---|---|---|
| 张三 | 数学 | |
| 李四 | 数学 | |
| ← 王五消失了! | ||
3. 数据为何消失?
Section titled “3. 数据为何消失?”- 第一步:执行 LEFT JOIN,先得到这个中间结果。
| 姓名 | 课程 |
|---|---|
| 张三 | 数学 |
| 张三 | 英语 |
| 李四 | 数学 |
| 王五 | NULL |
-
第二步:执行 WHERE 过滤
WHERE 成绩表.课程 = '数学'- 要求
课程字段必须等于'数学' - 但王五的
课程是NULL,NULL = '数学'的结果是 UNKNOWN(不是 true) - 所以王五这行被 过滤掉了!
4. 退化为内连接是什么意思?
Section titled “4. 退化为内连接是什么意思?”- 内连接(INNER JOIN) 的结果就是:只返回两表都匹配的记录。
- 上面错误写法的结果,和下面这个 INNER JOIN 完全一样:
SELECT 学生表.姓名, 成绩表.课程FROM 学生表INNER JOIN 成绩表 ON 学生表.学号 = 成绩表.学号WHERE 成绩表.课程 = '数学';虽然使用了
LEFT JOIN(本应保留主表所有记录,从表无匹配时填充NULL),但如果将从表的过滤条件写在WHERE子句中(WHERE是对连接后的完整结果集进行过滤),就会把那些从表为NULL的行排除掉。这样一来,最终结果与INNER JOIN完全一致,这种现象就称为 “外连接退化为内连接
| 过滤目标 | 推荐写法 | 说明 |
|---|---|---|
| 主表字段(如学生姓名、学号) | WHERE 或 ON 均可 | 不影响主表记录的完整性 |
| 从表字段(如课程、分数) | 必须写在 ON 中 | 若写在 WHERE,会导致无匹配的主表行被过滤,使 LEFT JOIN 退化为 INNER JOIN |
“想保留主表全部记录,从表的条件千万别放 WHERE 里!”