前两天写 SQL 时,本想通过 A LEFT JOIN B ON 后面的条件把查出的两条记录筛成一条,结果发现还是返回了两条。
后来才搞明白:LEFT JOIN 的 ON 条件不会过滤结果记录条数,它只会根据 AND 后面的条件决定是否显示右表(B 表)的记录,左表(A 表)的记录一定会显示出来。
不管 AND 后面写的是 A.id = 1 还是 B.id = 1,都会把 A 表中所有记录都查出来,同时按条件关联显示 B 表中对应的行。下面用学生表关联班级表来验证一下。
运行 SQL:
select * from student s left join class c on s.classId=c.id order by s.id

运行 SQL:
select * from student s left join class c on s.classId=c.id and s.name="张三"order by s.id

运行 SQL:
select * from student s left join class c on s.classId=c.id and c.name="三年级三班"order by s.id

数据库在通过连接两张或多张表返回记录时,都会先生成一张中间临时表,再把这张临时表返回给用户。
在使用 LEFT JOIN 时,ON 条件和 WHERE 条件的区别如下:
ON 条件是在生成临时表时使用的条件。它不管 ON 中的条件是否为真,都会返回左边表中的记录。
WHERE 条件是在临时表生成好之后,再对临时表进行过滤的条件。此时已经没有 LEFT JOIN 必须返回左表记录的含义了,条件不为真的行会被全部过滤掉。
假设有两张表:
表1:tab1
表2:tab2
| size |
name |
| 10 |
AAA |
| 20 |
BBB |
| 30 |
CCC |
两条 SQL:
1、
select * form tab1 left join tab2 on (tab1.size = tab2.size) where tab2.name=’AAA’
2、
select * form tab1 left join tab2 on (tab1.size = tab2.size and tab2.name=’AAA’)
第一条 SQL 的执行过程:
1、先按 ON 条件生成中间表:
tab1.size = tab2.size

2、再对中间表过滤 WHERE 条件:
tab2.name=’AAA’

第二条 SQL 的执行过程:
1、中间表 ON 条件:
tab1.size = tab2.size and tab2.name=’AAA’
条件不为真时,也会返回左表中的记录:

其实上面这些结果差异的关键原因,就在于 LEFT JOIN、RIGHT JOIN、FULL JOIN 的特殊性:不管 ON 上的条件是否为真,都会返回左表或右表中的记录,FULL JOIN 则兼具左表和右表的特性,取两者并集。而 INNER JOIN 没有这个特殊性,条件放在 ON 中还是 WHERE 中,返回的结果集是相同的。
这类 JOIN 条件差异的坑,在日常业务 SQL 里并不少见。更多 MySQL 实战经验,欢迎来云栈社区一起交流。
|