[zt] Oracle不使用索引的几种情况

本文详细探讨了Oracle数据库中索引使用的多种限制情况,包括与NULL值比较、聚集函数使用、函数转换、统计信息过时等问题,并通过实例验证了这些情况下的索引行为。

Oracle不使用b*tree索引的情况大致如下

1where条件中和null比较可能导致不使用索引

2countsumavemaxmin等聚集操作时可能导致不使用索引

3:显示或者隐式的函数转换导致不使用索引

4:在cbo模式下,统计信息过于陈旧导致不使用索引

5:组合索引中没有使用前导列导致没有使用索引

6:访问的数据量超过一定的比例导致不使用索引

下面就其中的几点做一些说明

一:Null可以使用索引吗

一般情况下,where条件中和null比较将会导致full table scan,实际上,如果table中索引建列的值都为null,那么该行在索引(此处指b*tree,位图索引和聚簇索引可以有空值)中就不会存在,因此oracle为了保证查询结构的准确性,就会用full table scan代替index scan,这样理解,不走索引也就在情理之中。

当然,如果某个索引列上有定义为not null,在这种情况下,不存在所有索引列都为空的情况,所以此种情况下,是可以走index scan的,因此,对于where条件中含有类似is null=null的情况,是否走索引,还是要看索引建中是否有某个列定义为not null

具体实验如下:

SQL> create table t(x char(3),y char(5));

SQL> insert into t(x,y) values ('001','xxxxx');

SQL> insert into t(x,y) values ('002',null);

SQL> insert into t(x,y) values (null,'yyyyy');

SQL> insert into t(x,y) values (null,null);

SQL> commit;

SQL> create unique index t_idx on t(x,y);

SQL> analyze table t compute statistics for table for all indexes;

SQL> select blevel,leaf_blocks,num_rows from user_indexes where index_name=upper('t_idx');

   BLEVEL LEAF_BLOCKS  NUM_ROWS

---------- ----------- ----------

        0          1         3

isnert四条记录,但索引只保存3条,最后一条没有保存在索引中

SQL> set autotrace traceonly explain;

SQL> select * from t where x is null;

Execution Plan

----------------------------------------------------------

  0     SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1 Bytes=8)

  1   0  TABLE ACCESS (FULL)OF 'T' (Cost=2 Card=1 Bytes=8)

 

SQL> create table t1(x char(3),y char(5) not null);

SQL> insert into t1(x,y) values ('001','xxxxx');

SQL> insert into t1(x,y) values (null,'xxxxx');

SQL> commit;

SQL> create unique index t1_idx on t1(x,y);

SQL> analyze table t1 compute statistics for table for all indexes;

SQL> select blevel,leaf_blocks,num_rows from user_indexes where index_name=upper('t1_idx');

   BLEVEL LEAF_BLOCKS  NUM_ROWS

---------- ----------- ----------

        0          1         2

SQL> select * from t1 where x is null;

Execution Plan

----------------------------------------------------------

  0     SELECT STATEMENT ptimizer=CHOOSE (Cost=1 Card=1 Bytes=11)

  1   0  INDEX (RANGE SCAN) OF 'T1_IDX' (UNIQUE)(Cost=1 Card=1 Byt

二:COUNT(*)等聚集函数可能导致不使用索引

在做countsumavemaxmin等聚集操作时,有的时候也会不用索引,因为如果优化器发现索引列没有任何一个列定义为not null而且where条件中也没有索引键列,如x=x,在此情况下,索引扫描结果会不准确,此时oracle就会用全表full table scan。沿用上面的二个表来说明

SQL> select count(*) from t;

Execution Plan

----------------------------------------------------------

  0     SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1)

  1   0  SORT (AGGREGATE)

  2   1    TABLE ACCESS (FULL) OF 'T'(Cost=2 Card=4)

 

SQL> select sum(x) from t;

Execution Plan

----------------------------------------------------------

  0     SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1 Bytes=5)

  1   0  SORT (AGGREGATE)

  2   1    TABLE ACCESS (FULL) OF 'T'(Cost=2 Card=4 Bytes=20)

因为该表的索引列(xy)没有定义为not null,所以都走了全表扫描,即使把x=nully=null的行删除,同样还是走全表扫描。

SQL> delete t where x is null and y is null;

已删除1行。

Commit

SQL> analyze table t compute statistics for table for all indexes;

SQL> select count(*) from t;

Execution Plan

----------------------------------------------------------

  0     SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1)

  1   0  SORT (AGGREGATE)

  2   1    TABLE ACCESS (FULL) OF 'T'(Cost=2 Card=3)

 

同样的sql语句,对于t1表,因为索引列y定义为not null,所以oracle会选择index scan

SQL> select count(*) from t1;

 

Execution Plan

----------------------------------------------------------

  0     SELECT STATEMENT ptimizer=CHOOSE (Cost=1 Card=1)

  1   0  SORT (AGGREGATE)

  2   1    INDEX (FULL SCAN) OF 'T1_IDX' (UNIQUE)(Cost=1 Card=2)

SQL> select sum(x) from t1;

Execution Plan

----------------------------------------------------------

  0     SELECT STATEMENT ptimizer=CHOOSE (Cost=1 Card=1 Bytes=5)

  1   0  SORT (AGGREGATE)

  2   1    INDEX (FULL SCAN) OF 'T1_IDX' (UNIQUE)(Cost=1 Card=2 By

         tes=10)

三:隐式或者显示的函数转换降导致全表扫描

SQL> Select * from t1 where x=001;

X     Y         COMM

------ ---------- ----------------------------

001   xxxxx     88888

Execution Plan

----------------------------------------------------------

  0     SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1 Bytes=26)

  1   0  TABLE ACCESS (FULL) OF 'T1'(Cost=2 Card=1 Bytes=26)

因为xchar类似,在这里oraclex=001做了隐式转换to_number(x)=001,建在该字段的索引将不起作用,基于函数的索引(function based index)可以在此派上用场,相对于普通索引,fbi是把经过函数转换后的值存放到索引中

SQL> create index t1_fbi on t1(to_number(x));

SQL> analyze table t1 compute statistics for table for all indexes;

SQL> Select * from t1 where x=001;

X     Y         COMM

------ ---------- ----------------------------

001   xxxxx     88888

Execution Plan

----------------------------------------------------------

  0     SELECT STATEMENT ptimizer=CHOOSE (Cost=1 Card=1 Bytes=26)

  1   0  TABLE ACCESS (BY INDEX ROWID) OF 'T1' (Cost=1 Card=1 Bytes

         =26)

  2   1    INDEX (RANGE SCAN) OF 'T1_FBI'(NON-UNIQUE) (Cost=1 Card

         =1)

四:统计信息不是最新的,导致无法使用

五:组合索引中没有用到前导列导致没有用索引,如组合索引(xy,where条件类似where y=….,此时不走索引(如果x的不同值很少,那么oracle9i以后就有可能走index skip scan,其原理类似于select * from t where y=…and x=(某个确定的值) union all select * from t where y=…and x=(某个确定的值)……..

六:访问的数据比例超过一定范围,优化器会认为full table scan的成本更低,此事走索引扫描反而会使总成本变大,因此,索引用来快速访问表中的少量记录,对于访问表中的大量记录是不适合用索引的。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/35489/viewspace-604538/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/35489/viewspace-604538/

select "ID","RWBH","RWMJ","ZYFS","JHKSSJ","JHJSSJ","ZXR","ZT","BSM","RWLX","RWMC","DWBM","HSZP","PBZT","PBJG","LONGITUDE","LATITUDE","YQKSSJ","YQWCSJ","YXJ","HXID","SFJKFW","PROVINCE","CITY","COUNTY","TOWN","VILLAGE","XXDZ","HXXH","SFTH","THYY","JKNFCJ","JKBNCJYY","BZ","ACCOUNT","ZXDYMC","ZXDYCY","CREATE_BY_","CREATE_TIME_","D_YEAR","D_MONTH","D_DAY","D_WEEK","D_WEEKNUM","JCBH","RWID","ZRWID","YPDZ","SCSJ","DDXMMCS" from ( SELECT c."ID", c."RWBH", c."RWMJ", c."ZYFS", c."JHKSSJ", c."JHJSSJ", c."ZXR", c."ZT", c."BSM", 'd1' rwlx, c.cqmc rwmc, null dwbm, c.hszp, c.pbzt,c.pbjg, c.longitude, c.latitude, c.yqkssj, c.yqwcsj, c.yxj, NULL hxid, NULL sfjkfw, null PROVINCE, null city, null COUNTY, null town, null VILLAGE, null xxdz, null hxxh, c.sfth,c.thyy,c.jknfcj,c.jkbncjyy,c.bz, c.account, ( SELECT v.zxdymc FROM zxdysj_vw v WHERE v.ZXDYBH = c.zxr ) zxdymc, ( SELECT wm_concat ( to_char( r.account || '/' || r.xm ) ) FROM zxdysj_vw v, RYHJZGLB g, ryxxb r WHERE v.id = g.jzid AND g.ryid = r.id AND v.ZXDYBH = c.zxr ) zxdycy, c.create_by_, c.create_time_, to_char( c.jhkssj, 'yyyy' ) d_year, to_char( c.jhkssj, 'fmmm' ) d_month, to_char( c.jhkssj, 'fmdd' ) d_day, to_char( c.jhkssj, 'd' ) d_week, to_char( c.jhkssj, 'fmiw' ) d_weeknum, ( SELECT LISTAGG ( TO_CHAR( j.JCBH ), ',' ) WITHIN GROUP ( ORDER BY jg.CJRWCID ) FROM CJZYGLB jg, CJRWQFBB j WHERE jg.CJRWCID = c.ID AND jg.JCID = j.ID ) AS jcbh, g.jcrwid rwid,g.jczrwid zrwid, (SELECT g.JCYPDZ FROM CTRWGLB gc,CJRWQFBB g WHERE c.ID = gc.CTRWID AND g.ID = gc.JCID AND g.deleted_ = 'N' AND gc.deleted_ = 'N' AND rownum=1 ) ypdz, (SELECT max(g.YPSCSJ) YPSCSJ FROM CTRWGLB gc,CJRWQFBB g WHERE c.ID = gc.CTRWID AND g.ID = gc.JCID AND g.deleted_ = 'N' AND gc.deleted_ = 'N') scsj, g.name ddxmmcs FROM cjrwcb c JOIN (select wm_concat(distinct to_char(x.ddmc||'-'||r.name)) name,wm_concat (DISTINCT to_char( g.jcrwid )) jcrwid ,wm_concat (DISTINCT to_char( g.jczrwid ) ) jczrwid,cjrwid from rwcchrwcglb g,qjrwxxb r,ddxxb x where g.xmid=r.id and r.ddid=x.id and g.deleted_='N' and r.deleted_='N' and r.xfzt!='d0' and x.deleted_='N' and x.ddzt!='d0' GROUP BY cjrwid) g on g.cjrwid = c.id WHERE c.deleted_ = 'N' UNION ALL--全景任务数据 SELECT c."ID", c."RWBH", NULL rwmj, c."ZYFS", c."JHKSSJ", c."JHJSSJ", c."ZXR", c."ZT", c."BSM", 'd3' rwlx, d.dwmc rwmc, d.dwbm, c.hszp, c.pbzt,c.pbjg, c.longitude, c.latitude, c.yqkssj, c.yqwcsj, c.yxj, c.hxid, c.sfjkfw, c.PROVINCE, c.city, c.COUNTY, c.town, c.VILLAGE, c.xxdz, c.hxxh, c.sfth,c.thyy,c.jknfcj,c.jkbncjyy,c.bz, c.account, ( SELECT v.zxdymc FROM zxdysj_vw v WHERE v.ZXDYBH = c.zxr ) zxdymc, ( SELECT wm_concat ( to_char( r.account || '/' || r.xm ) ) FROM zxdysj_vw v, RYHJZGLB g, ryxxb r WHERE v.id = g.jzid AND g.ryid = r.id AND v.ZXDYBH = c.zxr ) zxdycy, c.create_by_, c.create_time_, to_char( c.jhkssj, 'yyyy' ) d_year, to_char( c.jhkssj, 'fmmm' ) d_month, to_char( c.jhkssj, 'fmdd' ) d_day, to_char( c.jhkssj, 'd' ) d_week, to_char( c.jhkssj, 'fmiw' ) d_weeknum, ( SELECT LISTAGG ( TO_CHAR( j.JCBH ), ',' ) WITHIN GROUP ( ORDER BY jg.CJRWCID ) FROM CJZYGLB jg, CJRWQFBB j WHERE jg.CJRWCID = c.ID AND jg.JCID = j.ID ) AS jcbh,to_char(g.jcid) rwid, NULL zrwid, (SELECT p.ypdz FROM QJCTRWB p WHERE c.id=p.CJRWCID AND p.DELETED_='N' AND rownum=1 ) ypdz, (SELECT max(p.create_time_) FROM QJCTRWB p WHERE c.id=p.CJRWCID AND p.DELETED_='N' ) scsj,g.name ddxmmcs FROM QJCJRWCB c join (select wm_concat(distinct to_char(x.ddmc||'-'||r.name)) name,CJRWCID,JCID from DWCJRWCGLB g,qjrwxxb r,ddxxb x where g.rwxxid=r.id and r.ddid=x.id and g.deleted_='N' and r.deleted_='N' and r.xfzt!='d0' and x.deleted_='N' and x.ddzt!='d0' GROUP BY CJRWCID,JCID) g on g.cjrwcid = c.id join CJJCDWB d on g.jcid = d.id WHERE c.deleted_ = 'N' AND d.deleted_ = 'N' ) t 根据这个视图sql,如果我只想查出这个视图的总记录数据,可以简化成总数查询吗,要保持总数量一致 现在用这个视图查询要24秒: SELECT count(t.ID) FROM TYKJ_ERP.DDRWSJ_VW t
最新发布
09-13
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值