日期:2014-05-16  浏览次数:20449 次

DB2中exists与in的效率对比 — 5秒与21分钟的差距

在SQL查询优化中,无论是ORACLE,SQL Server,还是DB2,或者其他数据库产品,都有这样一条优化规则:尽量使用exists去替代查询中的in。

但是关于他们之间的查询效率,并没有给出太多的实际查询对比,都只是看书上写,或者听“牛人”说,就信以为真了;

那么他们之间的效率差距到底有多大呢?我们来看下吧!

前几天一同事,需要找些数据,然后自己尝试写了这个查询语句,写了一会,写不下去了,然后向我“求救”。

她的需求是这样的:

1. 查找这样的卡号,卡号对应的账户,账户状态正常(为'0'),账户扩展标志前5为也正常('00000');

2. 卡号为已经领取的卡(为'1');

3. 开卡机构所属的城市代码不为'790'。

这个需求中,涉及到4张表,屏蔽写信息:

卡管理物理文件(表):(Key—卡号,存储卡号,以及卡的各种状态)(as400上的物理文件,相当于DB2中的表)

卡号账号对照表:(Key—账号,通过卡号,取到对应的账号,同时存储卡号和账号)

活期存款帐户动态表: (Key—客户号,存储账户信息,包括账户状态,账号;账号与卡号账号对照表关联)

网点(营业机构)信息 表:(Key—网点,存储网点信息,开卡机构与卡管理表关联)

我们先将上面的需求分解,不管多么复杂的需求,分解之后,都变得简单,下面的查询中屏蔽了一些敏感信息;

开卡机构所属的城市代码不为'790':

select e.网点                       
  from 网点信息表 e                  
 where e.城市代码 <> '790'

账户状态正常,账户扩展标志前5位为'00000':

select a.卡号 crdno            
  from 卡号账号对照表 a,活期存款账户动态表 b        
 where a.账号 = b.账号         
   and b.账户状态 = '0'              
   and b.账户扩展标志 like '000000%'

然后将这些分支SQL组合起来,就实现了需求。

select c.*                         
  from 卡管理表 c,(                  
select a.卡号 crdno            
  from 卡号账号对照表 a,活期存款账户动态表 b        
 where a.账号 = b.账号         
   and b.账户状态 = '0'              
   and b.账户扩展标志 like '000000%') d 
 where c.卡号 = d.crdno        
   and c.卡流转状态='1'             
   and c.开卡网点 in (            
select e.网点                       
  from 网点信息表 e                  
 where e.城市代码 <> '790')

然后执行,真希望马上就能出数据,可是:

时间一分一秒的过去,为了看下这条语句能运行多长时间,我还特意复制了数据处理情况;

结果大约2012-11-21 19:27的时候,这条语句才运行结束,数据出来了,开始运行的大概时间为:2012-11-21 19:05~19:10;

也就是说,这条sql查询运行了大概20分钟!哇!

这是21号运行的时间结果,今天我又重新运行了一下这个语句,运行情况如下:

开始:2012-11-24 15:35:00
处理状态:
F3=Exit   F4=Prompt   F6=Insert line   F9=Retrieve   F10=Copy line 
F12=Cancel            F13=Services     F24=More keys               
Query running. 28148734 records selected, 20370384 processed. 
Query running. 31133092 records selected, 22545088 processed.
Query running. 34895061 records selected, 25299477 processed.
Query running. 40745687 records selected, 29560372 processed.
Query running. 50737215 records selected, 36759514 processed. 
15minutes
Query running. 53872254 records selected, 39018439 processed. 
16minutes
Query running. 59911556 records selected, 43478566 processed.  
17minutes
Query running. 64619276 records selected, 46935096 processed. 
18minutes
Query running. 70084019 records selected, 50945419 processed.
19minutes
Query running. 74880947 records selected, 54461392 processed. 
20minutes
Query running. 79317109 records selected, 57668874 processed.
21minutes
Query running. 86032895 records selected, 63412911 processed.
结束:2012-11-24 15:56:05

对于这样的sql语句,真让人头疼,费那么大的劲才把数据读出来,真有点黄花菜都凉了的感觉;

下面我们来看下,高效率的sql吧!同一个查询语句,只需稍微改动,将in替换为exists,效率可是高得惊人哦!

  select c.*
  from 卡管理文件 c,(
select a.卡号 crdno
  from 卡号账号对照表 a,活期存款账户动态表 b
 where a.账号 = b.账号
   and b.账户状态 = '0'
   and b.账户扩展标志 like '000000%' ) d
 where c.卡号 = d.crdno
   and c.卡流转状态='1'
   and not exists (
select 1
  from 网点信息表 e
 where c.开卡网点 = e.网点
   and e.城市代码 = '790'
)

每次执行这个查询,不到5秒钟,数据就出来了,真的不敢相信!读取到的是同样的数据,效率差距怎么就这么大呢!

5秒 vs 21分钟

下面是这些表中的数据统计:

select count(*) from 卡管理文件
....+....1....
  COUNT ( * ) 
    4,837,343 
---------------------------
SELECT count(*) FROM 卡管理文件 WHERE 卡流转状态 ='1'  
....+....1.... 
  COUNT ( * )  
    4,340,909  
---------------------------
select count(*) from 卡号账号对找表
....+....1...
  COUNT ( * )
    4,786,131
---------------------------
select count(*) from 活期存款账户动态表
....+....1....
  COUNT ( * ) 
   24,325,998 
---------------------------
select count(*) fro