共计 3029 个字符,预计需要花费 8 分钟才能阅读完成。
1、in 和 exists
in 是把外表和内表作 hash(字典集合) 连接,而 exists 是对外表作循环,每次循环再对内表进行查询。一直以来认为 exists 比 in 效率高的说法是不准确的,如果查询的两个表大小相当,那么用 in 和 exists 差别不大;如果两个表中一个较小一个较大,则子查询表大的用 exists,子查询表小的用 in。
例如:表 A(小表),表 B(大表)
方式一:索引使用
1)select * from A where id in(select id from B) –> 效率低,用到了 A 表上 id 列的索引
2)select * from A where exists(select id from B where id=A.id) –> 效率高,用到了 B 表上 id 列的索引
3)select * from B where id in(select id from A) –> 效率高,用到了 B 表上 id 列的索引
4)select * from B where exists(select id from A where id=B.id) –> 效率低,用到了 A 表上 id 列的索引
方式二:遍历使用
1)in() 只执行一次,它查出 B 表中的所有 id 字段并缓存起来。然后检查 A 表的 id 是否与 B 表中的 id 相等,如果相等则将 A 表的记录加入结果集中,直到遍历完 A 表的所有记录。
它的查询过程类似于以下代码的执行过程:
List resultSet = {};
Array A=(select * from A);
Array B=(select id from B);
for(int i=0;i<A.length;i++) {
for(int j=0;j<B.length;j++) {
if(A[i].id==B[j].id) {
resultSet.add(A[i]);
break;
}
}
}
return resultSet;
可以看出,当 B 表数据较大时不适合使用 in(),因为它会把 B 表数据全部遍历一次。
如:A 表有 10000 条记录,B 表有 1000000 条记录,那么最多有可能遍历 10000*1000000 次,效率很差。
如:A 表有 10000 条记录,B 表有 100 条记录,那么最多有可能遍历 10000*100 次,遍历次数大大减少,效率大大提升。
结论:in() 适合 B 表比 A 表数据小的情况
2)exists() 会执行 A.length 次,它并不缓存 exists() 结果集,因为 exists() 结果集的内容并不重要,重要的是其内查询语句的结果集空或者非空,空则返回 false,非空则返回 true。
它的查询过程类似于以下代码的执行过程:
List resultSet={};
Array A=(select * from A);
for(int i=0;i<A.length;i++) {
if(exists(A[i].id) {// 执行 select id from B where B.id=A.id 是否有记录返回
resultSet.add(A[i]);
}
}
return resultSet;
当 B 表比 A 表数据大时适合使用 exists(),因为它没有那么多遍历操作,只需要再执行一次查询就行。
如:A 表有 10000 条记录,B 表有 1000000 条记录,那么 exists() 会执行 10000 次去判断 A 表中的 id 是否与 B 表中的 id 相等。
如:A 表有 10000 条记录,B 表有 100000000 条记录,那么 exists() 还是执行 10000 次,因为它只执行 A.length 次,可见 B 表数据越多,越适合 exists() 发挥效果。
再如:A 表有 10000 条记录,B 表有 100 条记录,那么 exists() 还是执行 10000 次,还不如使用 in() 遍历 10000*100 次,因为 in() 是在内存里遍历比较,而 exists() 需要查询数据库,我们都知道查询数据库所消耗的性能更高,而内存比较很快。
结论:exists() 适合 B 表比 A 表数据大的情况。
当 A 表数据与 B 表数据一样大时,in 与 exists 效率差不多,可任选一个使用。
2、not in 和 not exists
not in 逻辑上不完全等同于 not exists,如果你误用了 not in,小心你的程序存在致命的 bug。
请看下面的例子:
create table A1 (c1 int,c2 int);
create table A2 (c1 int,c2 int);
insert into A1 values(1,2);
insert into A1 values(1,3);
insert into A2 values(1,2);
insert into A2 values(1,null);
select * from A1 where c2 not in(select c2 from A2); –> 执行结果:无(null)
select * from A1 where not exists(select c2 from A2 where A2.c2=A1.c2); –> 执行结果:1 3
正如所看到的,not in 出现了不期望的结果集,存在逻辑错误。使用 not in(它会调用子查询),而使用 not exists(它会调用关联子查询)。如果子查询中返回的任意一条记录含有空值,则查询将不返回任何记录。如果子查询字段有非空限制,这时可以使用 not in。
如果查询语句使用了 not in,那么对内外表都进行全表扫描,没有用到索引;而 not exists 的子查询依然能用到表上的索引。所以无论哪个表大,用 not exists 都比 not in 要快。
3、in 和 =
select name from employee where name in(‘ 张三 ’,’ 李四 ’,’ 王五 ’);
与
select name from employee where name=’ 张三 ’ or name=’ 李四 ’ or name=’ 王五 ’;
的结果是相同的。
4.exists 防止插入重复记录
有时需要插入非重复记录,在 Mysql 中可以使用 ignore 关键字来忽略已有记录,但是其只能通过主键忽略,不能根据自定义条件忽略。
其语法为:insert ignore into tableName (column1,column2,……) values (value1,value2,……);
但是其他数据库不一定提供类似 ignore 关键字,所以可以使用 exists 条件句防止插入重复记录。
insert into A (name,age) select name,age from B where not exists (select 1 from A where A.id=B.id);
5. 关于 exists 更多说明
exists 用于检查子查询返回的结果集是否为空,该子查询实际上并不返回任何数据,而是返回值 true 或 false。
语法:exists subQuery
参数:subQuery 是一个受限的 select 语句 (不允许有 compute 子句和 into 关键字)。
结果类型:boolean 如果子查询包含行,则返回 true,否则返回 false。
结论:select * from A where exists (select 1 from B where A.id=B.id);
一种通俗的可以理解为:将外查询表的每一行,代入内查询作为检验,如果内查询返回的结果集非空,则 exists 子句返回 true,这一行方可作为外查询的结果行,否则不能作为结果。
——– 以上 sql 内容根据网上提供的资料整理出的结果,均适用与 Mysql、Sql Server、Oracle。
本文永久更新链接地址 :http://www.linuxidc.com/Linux/2016-04/130285.htm