in 和 exists的区别 用数据说话

1、环境

操作系统:winxp系统 cpu:p8700 双核2.53 内存:2GB 数据库:oracle9i

 

2、表结构

 

sql代码:
Sql代码   收藏代码
  1. drop table base_customer;  
  2.   
  3. create table base_customer  
  4. (  
  5.    uuid                 number(10) not null,  
  6.    customerId           varchar(20),  
  7.    showName             varchar(30),  
  8.    trueName             varchar(30),  
  9.    image                varchar(100),  
  10.    pwd                  varchar(50),  
  11.    registerTime         timestamp,  
  12.    securityKey          varchar(10),  
  13.    primary key (uuid),  
  14.    unique(customerId)  
  15. );  
  16.   
  17. create index idx_customer_registerTime on base_customer(registerTime);  
  18.   
  19. drop table base_customer_sign;  
  20. create table base_customer_sign  
  21. (  
  22.    uuid                  number(10) not null,  
  23.    signCustomerUuid       number(10) not null,  
  24.    signTime              timestamp not null,  
  25.    signCount             number(10) not null,  
  26.    signSequenceCount     number(10) not null,  
  27.    primary key (uuid)  
  28. );  
  29. create index idx_sign on base_customer_sign(signCustomerUuid);  

 

3、索引及数据量

 

sql代码:
Sql代码   收藏代码
  1. base_customer      100w条  
  2.   uuid主键  
  3.   customerId 唯一索引  
  4. base_customer_sign 100条  
  5.   uuid主键  
  6.   signCustomerUuid 非唯一索引  

 

4、初始化数据用例

 

java代码:
Java代码   收藏代码
  1. import java.sql.Connection;  
  2. import java.sql.DriverManager;  
  3. import java.sql.PreparedStatement;  
  4. import java.sql.Timestamp;  
  5. import java.util.Date;  
  6.   
  7. import oracle.jdbc.OracleDriver;  
  8.   
  9. public class Test {  
  10.       
  11.       
  12.     public static void main(String[] args) throws Exception {  
  13.         initData();  
  14.     }  
  15.       
  16.     public static void initData() throws Exception {  
  17.         DriverManager.registerDriver(new OracleDriver());  
  18.         Connection conn = null;  
  19.         try {  
  20.             String url = "jdbc:oracle:thin:@localhost:1521:orcl2";  
  21.             String username = "test";  
  22.             String password = "test";  
  23.             conn = DriverManager.getConnection(url, username, password);  
  24.             conn.setAutoCommit(false);  
  25.               
  26.             conn.createStatement().execute("truncate table base_customer");  
  27.               
  28.             PreparedStatement psst = conn.prepareStatement("insert into base_customer values(?,?,?,?,?,?,?,?)");  
  29.   
  30.             for(int i=1; i<=1000000;i++) {//100w  
  31.                 int count = 1;  
  32.                 psst.setInt(count++, i);  
  33.                 psst.setString(count++, "user" + i);  
  34.                 psst.setString(count++, "user" + i);  
  35.                 psst.setString(count++, "user" + i);  
  36.                 psst.setString(count++, "user" + i);  
  37.                 psst.setString(count++, "user" + i);  
  38.                 psst.setTimestamp(count++, new Timestamp(System.currentTimeMillis()));  
  39.                 psst.setString(count++, "key" + i);  
  40.                 psst.addBatch();  
  41.                 psst.executeBatch();  
  42.                 conn.commit();  
  43.             }  
  44.               
  45.             PreparedStatement psst2 = conn.prepareStatement("insert into base_customer_sign values(?,?,?,?,?)");  
  46.   
  47.             for(int i=1; i<=100;i++) {//100  
  48.                 int count = 1;  
  49.                 psst2.setInt(count++, i);  
  50.                 psst2.setInt(count++, i);  
  51.                 psst2.setTimestamp(count++, new Timestamp(System.currentTimeMillis()));  
  52.                 psst2.setInt(count++, 1);  
  53.                 psst2.setInt(count++, 1);  
  54.                 psst2.addBatch();  
  55.                 psst2.executeBatch();  
  56.                 conn.commit();  
  57.             }  
  58.               
  59.             psst.close();  
  60.             conn.commit();  
  61.         } catch (Exception e) {  
  62.             e.printStackTrace();  
  63.             conn.rollback();  
  64.               
  65.         } finally {  
  66.             conn.close();  
  67.         }  
  68.     }  
  69.   
  70. }  

 

 

5、场景

 

5.1、第一组 内表大 外表小

 

 

用例1、

 

sql代码:
Sql代码   收藏代码
  1. select count(*) from base_customer_sign where signCustomerUuid in (select uuid from base_customer where trueName like 'user%')  

 

执行计划:

如图1-1


执行时间:

   0.015秒

结论:数据库执行了优化,根本不是我们需要的用例。

 


用例2

 

java代码:
Java代码   收藏代码
  1. select count(*) from base_customer_sign where signCustomerUuid in (select uuid from base_customer where trueName like 'user%' group by uuid)  

执行计划:

如图1-2


执行时间:

   28.672秒

结论:内表如果查询回来很多数据并要排序的话,效率很极低,因此内表适合返回数据量小的表,例外是用例1场景。

 

 

 

用例3


java代码:
Java代码   收藏代码
  1. select count(*) from base_customer_sign  
  2. where exists (select 1 from base_customer where trueName like 'user%' and base_customer.uuid = base_customer_sign.signCustomerUuid)  
  3.    



执行计划:

如图1-3

in 和 exists的区别 用数据说话_第1张图片

执行时间:

   0.016秒

结论:外表执行全扫描,如果外表大很降低效率。

 

用例4

 

java代码:
Java代码   收藏代码
  1. select count(*) from base_customer_sign where signCustomerUuid in (select uuid from base_customer where customerId like 'user%')  

 

执行计划:

如图1-4

in 和 exists的区别 用数据说话_第2张图片

执行时间:

   13.61秒

结论:即使内表很小,但外表数据量很大 同样是低效。

 

用例5

 

java代码:
Java代码   收藏代码
  1. select count(*) from base_customer_sign  
  2. where exists (select 1 from base_customer where base_customer.customerId like 'user%' and base_customer.uuid = base_customer_sign.signCustomerUuid)  

 

执行计划:

 

如图1-5


执行时间:

   0.032秒

结论:内表大,外表小,速度快。

 

 

 

 

第二组 内表小 外表大

 

用例6

 

java代码:
Java代码   收藏代码
  1. select * from base_customer where uuid in (select signCustomerUuid from base_customer_sign where trueName like 'user%')  

 

执行计划:

如图1-6

in 和 exists的区别 用数据说话_第3张图片

执行时间:

   3.844秒

结论:外表全扫描,慢。

 

 

用例7


java代码:
Java代码   收藏代码
  1. select count(*) from base_customer  
  2. where exists (select 1 from base_customer_sign where trueName like 'user%' and base_customer.uuid = base_customer_sign.signCustomerUuid)  
  3.    



执行计划:

如图1-7

in 和 exists的区别 用数据说话_第4张图片

执行时间:

   3.828秒

结论:和用例6一样。

 

 

 

用例8

 

java代码:
Java代码   收藏代码
  1. select count(*) from base_customer where uuid in (select signCustomerUuid from base_customer_sign where uuid>1 and signSequenceCount < 1)  

 

执行计划:

如图1-8

in 和 exists的区别 用数据说话_第5张图片

执行时间:

   0.031秒

结论:sql被优化,使用base_customer_sign作为外表,而且和内表是通过连接搞定,效率快

 

 

用例9

 

java代码:
Java代码   收藏代码
  1. select count(*) from base_customer  
  2. where exists (select 1 from base_customer_sign where base_customer_sign.uuid>1 and base_customer.uuid = base_customer_sign.signCustomerUuid)  

 

执行计划:

如图1-9

in 和 exists的区别 用数据说话_第6张图片


执行时间:

   3.531秒

结论:外表全表扫描快不了。

 

 

 

 

总结:

1、 in可能被优化为 连接

2、 in 在未被优化时,外表小,内表大时(要建临时表并排序 耗时) 效率低

3、 exists 外表数据量大,速度肯定慢,,即使是in同样一样,而对于内表数据量多少跟索引有关。

4、 in 和 exists 在外表返回的数据量很大时也是低效的。

 

因此,,外表(驱动表) 应该都尽可能的小。

 

 

5、 not in 不走索引的,因此不能用

6、 not exists走索引的。

 

 

自己总结,难免有纰漏 本人只测试以上9个简单的用例,复杂场景可能未考虑到,因此在调优时 应该会看执行计划,根据执行计划决定哪个是高效的。

 

http://sishuok.com/forum/posts/list/1154.html

声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。

你可能感兴趣的:(java,oracle,sql,优化,user,ITeye)