• SQL优化——IN和EXISTS谁的效率更高


    IN和EXISTS被频繁使用在SQL中,虽然作用是一样的,但是在使用效率谁更高这点上众说纷纭。下面我们就通过一组测试来看,在不同场景下,使用哪个效率更高。

    测试数据:

    B表: 大表,大约300000行数据

    CREATE TABLE `B` (
     `id` int NOT NULL AUTO_INCREMENT,
     `B_id` int NOT NULL,
     `value` varchar(20) NOT NULL,
     `flag` int not null,
      PRIMARY KEY (`id`),
      KEY `idx_b_flag` (`flag`),
      KEY `idx_b_id` (`B_id`)
    )


    A表: 小表,20000行数据

    CREATE TABLE `A` (
     `id` int NOT NULL AUTO_INCREMENT,
     `flag` int NOT NULL,
     `value` varchar(20) NOT NULL,
     PRIMARY KEY (`id`),
     KEY `idx_a_flag` (`flag`)
    )

    测试1:

    子查询 select flag from B where B_id<100 结果集99条。

    select * from A where flag in (select flag from B where B_id<100 );198 rows in set (0.00 sec)select * from A where exists (select * from B where B_id<100 and A.flag=B.flag);198 rows in set (0.10 sec)

    可以看到本次测试IN效率高于EXISTS。

    再看执行计划:

    图片

    IN的执行计划:

    (1)执行A表的查询,查询条件是A.flag在结果集B里面,可以使用到A表的索引flag;

    (2)执行B表的子查询,得到结果集B,可以使用到B表的索引B_id。

    图片

    EXISTS的执行计划:

    (1)先将A表所有记录取到;

    (2)逐行针对A表的记录,去关联B表,判断B表的子查询是否有返回数据,5.5之后的版本使用Block Nested Loop(Block 嵌套循环);

    (3)如果子查询有返回数据,则将A当前记录返回到结果集。

    A相当于取全表数据遍历,B可以使用到索引。

    测试2

    子查询 select flag from B where B_id>100 结果集 299899条。

    select * from A where flag in (select flag from B where B_id>100 );19798 rows in set (0.09 sec)select * from A where exists (select * from B where B_id>100 and A.flag=B.flag);19798 rows in set (0.06 sec)

    可以看到本次EXISTS效率比IN高。

    再看执行计划:

    图片

    图片

    两者的索引使用情况与第一次实验是一致的,当子查询结果集很大,而外部表较小的时候,Exists的Block Nested Loop(Block 嵌套循环)的作用开始显现,查询效率会优于IN。

    从两次测试来看,并不能说明谁的效率更高,而应该具体情况具体分析:

    首先来看IN和EXISTS的执行原理:

    IN是做外表和内表通过Hash连接,先查询子表,再查询主表,不管子查询是否有数据,都对子查询进行全部匹配。

    EXISTS是外表做loop循环,先主查询,再子查询,然后去子查询中匹配,如果匹配到就退出子查询返回true,将结果放到结果集。

    IN原理

    在in()的执行中,先执行内表得到结果集,再执行外表。外表会对所有的内表结果集匹配,也就是说:如果外表有100,内表有10000,就会执行100*10000次。所以在内表比较大的时候,不合适用in()方法,效率比较低。

    select * from 外表 a where id in (select 相关id from 内表) IN的执行类似如下:

    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;

    EXISTS原理

    exists()的执行过程中,并没有对每一条内表的数据都进行查询,而是存在该条数据的时候会将结果集存起来,到最后的时候同一输出结果集。

    select a.* from 外表 a where exists(select 1 from 内表 b where a.id=b.id) 的EXISTS的执行语句如下:

    List resultSet=[];
    Array A=(select * from 外表 A)

    for(int i=0;i<A.length;i++) {
      if(exists(A[i].id) {    //执行select 1 from 内表 b where b.id=a.id是否有记录返回
          resultSet.add(A[i]);
      }
    }
    return resultSet;

    设:外表A,内表B。

    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()需要查询数据库,我们都知道查询数据库所消耗的性能更高,而内存比较很快。

    # 总结 #

    1、IN查询在内部表和外部表上都可以使用到索引;

    2、EXISTS查询仅内部表上可以使用到索引,外表会全表扫描;当子查询结果集很大,而外部表较小的时候,EXISTS的Block Nested Loop(Block 嵌套循环)的作用开始显现,查询效率会优于IN;

    3、当子查询结果集较小,而外部表很大的时候,EXISTS的Block嵌套循环优化效果不明显,IN 的外表索引优势占主要作用,此时IN的查询效率会优于EXISTS。

    子查询结果集越大用EXISTS,子查询结果集越小用IN。

    转载自数据和云公众号

    螃蟹在剥我的壳,笔记本在写我,漫天的我落在枫叶上雪花上,而你在想我。 --章怀柔
  • 相关阅读:
    博客园的使用和设置
    关于递推算法求解约瑟夫环问题P(n,m,k,s)
    HTML超链接和路径
    HTML文本元素标签
    HTML前序
    Java正则表达式验证至少6位表达式中至少包含数字大小写字母中的一种
    创建自己的第一个Vue项目
    查看端口及进程
    Java读取数据库(Oracle,MySQL,PostgresSQL)表信息以及字段信息生成Word文档
    启动项目报错ORA-12505, TNS:listener does not currently know of SID given in
  • 原文地址:https://www.cnblogs.com/lovezhr/p/15180578.html
Copyright © 2020-2023  润新知