阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

SQL查询中in、exists、not in、not exists的用法与区别

144次阅读
没有评论

共计 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

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计3029字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中