SQL语句中,过滤条件放在不同筛选器on、where和having的区别和联系。
蚂蚁金服的一道SQL面试题如下:SQL语句中,过滤条件放在on和where子句中的区别是什么? 当时满脑子是left join和inner join等,觉得没区别啊!当天晚上细思极恐,故梳理一下。
在多表连接查询语法中,最令人迷惑的非on和where这两个筛选器的区别莫属了。在编写查询SQL脚本的时候,筛选条件无论是放在on子句还是where子句,查询到的结果集总是一模一样的,既然如此,为什么还要让sql查询支持两种筛选器呢?这不是多此一举吗?其实,这两种筛选器在执行效率方面存在差异,只是如果不深度挖掘不容易发现罢了。
过滤条件放在on和where中的区别
数据库在通过连接两张或多张表来查询记录时,都会先通过join on子句生成一张中间的临时表,然后再在临时表中通过where子句过滤数据并将结果集返回给用户。在使用多表关联时,on和where子句的区别如下:
1、 on子句是在生成临时表时使用的。它不管on中的条件是否为真,都会返回驱动表中的记录;被驱动表成立就返回对应数据,不成立就赋值为null。
通俗地说 ,对于左外连接(left join)或者右外连接(right join)的驱动表来说,如果无法在被驱动表中找到匹配
ON
子句中的过滤条件的记录,那么该记录仍然会被加入到结果集中,对应的被驱动表记录的各个字段使用NULL
值填充。2、where子句是对已经生成的临时表进行过滤的条件,这时过滤临时表中全部条件不为真的记录。
需要注意的是内连接中的WHERE子句和ON子句是等价的。ON子句
是专门为外连接驱动表中的记录在被驱动表找不到匹配记录时应不应该把该记录加入结果集这个场景提出的,所以如果把ON子句
放到内连接中,MySQL
会把它和WHERE子句
一样对待。
在使用 INNER JOIN 时会产生一个结果集,WHERE 条件在这个结果集中再根据条件进行过滤,如果把条件都放在 ON 中,在 INNER JOIN 的时候就进行过滤了,比如
SELECT A.* FROM A
INNER JOIN B ON B.ID = A.ID AND B.State = 1 -- on子句可以写多个条件
INNER JOIN C ON B.ID = C.ID
在联查 B 表时,就过滤掉状态不等于 1 的记录,从而使得状态不等于 1 的记录就不需要去联查 C 表了,而
SELECT A.* FROM A
INNER JOIN B ON B.ID = A.ID
INNER JOIN C ON B.ID = C.ID
WHERE B.State = 1
则不考虑 B 的状态是否满足,都去联查 C生成临时表,最后再通过where子句将满足B 状态State = 1的记录查出来。综上所述,得出的结论就是把 过滤条件放入inner join on 比直接 where 的查询效率要高。
on、where、having的区别和联系
在写SQL语句的时候,我们经常会用到各种表连接(left join、right join、inner join和 full join等),还有各种分组聚合函数(sum、min、max、avg和count等)。那么我们在写连接操作SQL的时候,对于不同的过滤条件是放在 ON 子句,还是代表分组操作的 having 子句,抑或 where子句中呢?我们先看一下三种条件关键字的执行顺序:
on > where > 聚合函数 > having
详细的来讲,就是:
步骤一、根据on筛选器生成生成临时表。此时的临时表会因为left join或right join的特性而一定带有主表的记录,也就是主表的记录不会被 on 条件过滤掉。
步骤二、根据where筛选器过滤临时表。因为临时表已经生成完毕,根据where条件过滤时主表记录也会被过滤。
步骤三、聚合函数进行运算。
步骤四、 聚合函数运算完毕,having子句生效,对运算完毕的临时表进行过滤 ,生成最终的结果表。
三个筛选器on、where和having都可以加条件,on是最先执行,where次之,having最后。有时候如果这先后顺序不影响中间结果的话,那最终结果是相同的。但因为on是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的。
了解了上述执行过程后,我们来聊聊应该把过滤条件放在哪,即如何使用on、where和having筛选器。下面是一些基础的选择标准,供各位参考:
- 所有的连接条件都必需要放在ON子句。
- 如果在表关联后需要保留主表的所有记录,不论有没有相匹配的从表记录,那么我们就应该将过滤条件放在 on 中。
- 如果过滤条件需要在聚合函数运算完毕之后才能确定,比如我们想要找出平均分数大于60分的班级,那么就必须等待分组聚合函数执行完毕才能进行过滤,那这个过滤条件肯定就是放在having中了,因为where生效的时候聚合函数还没有进行运算。
- 如果过滤条件不依赖聚合函数,只是想要符合条件的部分记录,而且没有要求保留主表的全部记录,那么我们就应该放在where子句中。当然,如果表关联是采用inner join的话,因为没有主从表的关系,所以放在 where 和 on 中是一样的。
- 就执行效率来看:因为on生效最早,所以放在on中应该最快,其次是where,最后是having。