SQL优化 —— in与not in

1 in

文章从三方面介绍:查询集合,查询出错率,查询效率。主要讲什么时候可以用in,用in效果不佳时,用什么替代。总结出两点:

  • in后的查询集合不确定
    例如in (select…),应判断内查询与外查询的关系。当内查询的表小时(小于外查询的表),用in效率高。当外查询的表小时(小于内查询的表),用exists效率高。(因为in 先执行内查询,再执行外查询;而 exists先执行外查询再执行内查询)
  • in后的查询集合是确定且有限
    集合内的值连续时,应尽可能使用between …and 。
    集合内的值不连续时,可以用in,例如in (1,3,7)。

1.1 查询集合

in后的查询集合不确定时,例如in (select…),应判断内查询与外查询的关系。当内查询的表小时(小于外查询的表),用in效率高。当外查询的表小时(小于内查询的表),用exists效率高。(因为in 先执行内查询,再执行外查询;而 exists先执行外查询再执行内查询)
in后的查询集合是确定且有限的集合,且集合内的值连续时,应尽可能使用between …and 。
in后的查询集合是确定且有限的集合,但集合内的值不连续时,可以用in,例如in (1,3,7)。

1.2 查询出错率

SQL中用in 或 not in 容易出错,所以应尽量避免使用in或not in。什么时候可以使用?确定且有限的集合时,可以使用。如 IN (0,1,2)。

下面的例子参考自:SQL性能优化 - 避免使用 IN 和 NOT IN
在这里插入图片描述
在这里插入图片描述
一位大神曾经说过,如果是确定且有限的集合时,可以使用。如 IN (0,1,2)。
参考文献:[笔记] SQL性能优化 - 避免使用 IN 和 NOT IN

1.3 查询效率

一句话总结:in先执行子查询,再执行主查询;而exists先执行主查询,再执行子查询。

具体的:

in在查询的时候,首先查询子查询的表,然后将内表和外表做一个笛卡尔积,然后按照条件进行筛选。
exists先执行主查询,再根据主查询的结果,执行子查询。
具体例子,见参考文献。
应用场景:
当子查询的结果较小,且主查询的表较大且有索引时,应用in。
当主查询的结果较小,而子查询的表较大且有索引时,应用exists。

即先把大表减小,然后再去匹配另一个表。

in和exists的不同,即驱动顺序的不同(这是性能变化的关键)。如果是exists,那么以外层表为驱动表,先被访问。如果是IN,那么先执行子查询,以内层表为驱动表。
所以我们以驱动表的快速返回为目的(即越快获得驱动表,越好),那么就会考虑到索引及结果集的关系了 。另外IN时不对NULL进行处理。

in 是把外表和内表作hash 连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询。一直以来认为exists比in效率高的说法是不准确的。

参考文献:SQL中in和exists的区别

2 not in

2.1 查询集合中有null,不使用not in

如果查询集合中有null,不适合用not in,容易出错(查询结果为空)。例如:x not in (1,2,null)。

  1. 为什么x not in (1,2,null)会出错呢?
    因为x not in (1,2,null) 等价于x!=1 and x!=2 and x!=null ,而在sql中,对于任意x,x!=null永远为false,所以整体结果永远为false,所以查询结果永远为空。

  2. 为什么在sql中,x!=null为false呢?
    参考文献:SQL条件!=null查不出数据
    sql的逻辑表达式有三种可能值:true、false、unknown。
    其他语言的逻辑表达式都是只有两种可能值,而sql中有三种,这是sql特有的。从字面上理解unknown就是:什么都不知道。在sql中将任何值(包括null本身)与null作比较,都会返回unknown。而在查询表达式(having、where)中, unknown会被视为false。
    但是并不是所有情况下,都会将unknown视为false,在 check约束中,就会将unknown视为true。所以,在用check约束将字段设置为>=0时,还可以向该字段中插入null值,因为在check中,null>=0的结果unknown,会被视为true。

2.2 查询集合中有null,不使用not in,那用什么代替呢?

三种解决办法:

  1. 仍使用not in ,但修改sql语句。
    将in后的查询结果中的null过滤掉。
    例如:
select name from table1 where name not in (
 select name from table2 where name is not null
);
  1. 使用join 代替
select * 
from a left join b on a.id=b.aid
where b.aid is null
用and:
select Table_A.ID,Table_A.name 
from Table_A left join Table_B on Table_A.ID=Table_B.ID and Table_B.ID is null
用where:
select Table_A.ID,Table_A.name 
from Table_A left join Table_B on Table_A.ID=Table_B.ID where Table_B.ID is null
  1. 使用not exists代替
共有的不要了,要独有的。
select * from a 
where not exists(
     select 1 from b where a.col = b.col
);
  • 23
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张之海

若有帮助,客官打赏一分吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值