SQL关键字Exists的作用
背景:
一个业务需求,需要查询每个主版本下具有最新子版本的记录;
在寻找解决方案时,用到了groupby、having、连接查询等SQL基础知识。这里针对思考过程产生的一些问题进行记录:
关于GroupBy、Having、聚合函数
作用
- GroupBy:实现按某字段进行分组处理
- 聚合函数:提取每个组进行聚合后的一些全局信息。支持的聚合函数有:COUNT(),SUM(),AVG(),MIN(),MAX()
- Having:是针对每个组进行筛选的条件语句。可以在其中使用聚合函数,此时返回每个组对应的聚合信息;
-如MIN():不是返回具有最小值的组,而是返回每个组的最小值
使用:
- 一般进行分组查询时,会带上分组字段,来区分所查出的是哪个组的值;
如:
select min('age'),'gender' from 'student' group by 'gender';
- 如果查询的结果字段不带聚合函数,则会返回每个组中的一条(每个分组结果集的第一条)。
例如,对如下数据进行分组查询。
SELECT * FROM `temp` GROUP BY address
结果集为:
Exists字段
作用
Exists关键字的主要作用就是 检查SQL语句中的子查询是否至少会返回一行数据。当存在至少一行数据时返回true。
所以,使用EXISTS关键字的子查询其实并不返回数据,而是返回 布尔类型的true或 false。
使用
理论上来说,Exists 关键字后边的subquery(子查询,又名“相关子查询”) 可以以 SELECT * 或者 其他 任意字段 开头,比如可以使用:
SELECT * FROM t1 WHERE EXISTS (SELECT 'A' FROM t2);
或者
SELECT * FROM t1 WHERE EXISTS (SELECT '法外狂徒张三' FROM t2);
但是一般常用的形式是:
SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t2);
原理:执行顺序
以下SQL语句为例:
SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t1.id = t2.id);
顺序如下:
1 首先执行外部查询,并缓存结果集:
SELECT * FROM t1;
2 遍历外部查询结果集的每一行Record(记录),然后代入子查询中作为条件进行查询。
SELECT 1 FROM t2 WHERE t1.id = t2.id;
3 如果子查询有返回结果,则EXISTS子句返回true,则外部查询的这一行记录可作为结果行返回,否则不能作为结果==
基于执行顺序,理解上面常用的形式
1)对于主查询表中每一条记录,会去观察里面的子查询是否能查出记录;
2)而子查询的筛选条件中使用主查询该条记录的某些字段值作为条件值。
3)这就相当于:针对主查询表中的每条记录,生成了一个动态取值的、同时并用多字段的查询条件,来实现筛选。
针对前面业务需求的解法:
方式一 使用分组及having条件
select * from 'table' group by 'main' having min('sub');
对于这种方式,如果在组中存在多条记录的’sub’相同时,按min筛选会选出多条,此时会随机返回一条而不是将所有的都返回。
方式二 分两阶段查询
第1阶段 查出所有主版本具有的最小子版本号。结果将是“主版本,子版本”的一个集合。
select min('sub') ,'main' from 'table' group by 'main'
第2阶段, 遍历上面结果集合中每对‘主版本号+最小子版本号’,用其作为条件进行一次查询
select * from 'table' where 'main'=m[i] and 'sub'=s[i]
//视上面结果集中第一列sub为s,第二列main为m。
但这种方式需要查询的次数太多。
方式三 使用exists关键字+嵌套查询
select * from 'table' t1
where Exists (
select * from (
select min(sub) as esub ,main from 'table' group by 'main' ) as t2
where
t1.sub=t2.esub and t1.main=t2.main
);
说明:这里与上面执行流程中的给出的exist常用形式例子相比,相当于使用了一条子查询的结果集作为表2。
这个表2存放了所有的‘主版本-最小子版本’,按此SQL,如果表2中有表1中当前所遍历到的那条记录(即,那条记录满足了内层子查询的条件:主版本下-最小子版本),则子查询会查到数据,exist会返回true,此时外层会将此记录返回。
对比方式二:
这种方式相当于将方式二的第1步作为表2,在外层加了一层对表的查询来实现轮询,代替方式二的第2阶段。