菜鸟笔记
提升您的技术认知

索引失效场景-ag真人游戏

针对自己曾经经历过的一道面试题,那些情况不走索引,于是搜索网络和书籍的一些资料,整理如下:  

1、 查询谓词没有使用索引的主要边界,换句话说就是select *,可能会导致不走索引。 比如,你查询的是select * from t where y=xxx;假如你的t表上有一个包含y值的组合索引,但是优化器会认为需要一行行的扫描会更有效,这个时候,优化器可能会选择table access full,但是如果换成了select y from t where y = xxx,优化器会直接去索引中找到y的值,因为从b树中就可以找到相应的值。  

2、 单键值的b树索引列上存在null值,导致count()不能走索引。 如果在b树索引中有一个空值,那么查询诸如select count() from t 的时候,因为hashset中不能存储空值的,所以优化器不会走索引,有两种方式可以让索引有效,一种是select count(*) from t where xxx is not null或者把这个列的属性改为not null (不能为空)。  

3、 索引列上有函数运算,导致不走索引 如果在t表上有一个索引y,但是你的查询语句是这样子select * from t where fun(y) = xxx。这个时候索引也不会被用到,因为你要查询的列中所有的行都需要被计算一遍,因此,如果要让这种sql语句的效率提高的话,在这个表上建立一个基于函数的索引,比如create index idx funt on t(fun(y));这种方式,等于oracle会建立一个存储所有函数计算结果的值,再进行查询的时候就不需要进行计算了,因为很多函数存在不同返回值,因此必须标明这个函数是有固定返回值的。  

4、 隐式转换导致不走索引。 索引不适用于隐式转换的情况,比如你的select * from t where y = 5 在y上面有一个索引,但是y列是varchar2的,那么oracle会将上面的5进行一个隐式的转换,select * from t where to_number(y) = 5,这个时候也是有可能用不到索引的。  

5、 表的数据库小或者需要选择大部分数据,不走索引 在oracle的初始化参数中,有一个参数是一次读取的数据块的数目,比如你的表只有几个数据块大小,而且可以被oracle一次性抓取,那么就没有使用索引的必要了,因为抓取索引还需要去根据rowid从数据块中获取相应的元素值,因此在表特别小的情况下,索引没有用到是情理当中的事情。

6、 cbo优化器下统计信息不准确,导致不走索引 很长时间没有做表分析,或者重新收集表状态信息了,在数据字典中,表的统计信息是不准确的,这个情况下,可能会使用错误的索引,这个效率可能也是比较低的。

7、!=或者 <>(不等于),可能导致不走索引,也可能走 index fast full scan 例如select id  from test where id<>100 8 、表字段的属性导致不走索引,字符型的索引列 会导致优化器认为需要扫描索引大部分数据且聚簇因子很大,最终导致弃用索引扫描而改用全表扫描方式 , 由于字符型和数值型的在insert的时候排序不同, 字符类型 导致了聚簇因子很 大,原因是插入顺序与排序顺序不同。

详细点说,就是按照数字类型插入(1..3200000),按字符类型('1'...'32000000')t排序,在对字符类型使用大于运算符时,会导致优化器认为需要扫描索引大部分数据且聚簇因子很大,最终导致弃用索引扫描而改用全表扫描方式。 下面展示测试结果, 两个表的数据类型相似(只是id字段类型不同),各插入了320万数据,id字段范围为1~3200000。


模拟场景   相关代码如下: 


对于普通的采用数值类型的字段,范围查询就是正常的索引范围扫描,执行效率很高。 


对于文本类型字段的表,范围查询就是对应的全表扫描,效率较低是显而易见的 。   解决方法 


将sql语句由开放区间扫描(>=),修改为封闭区间(between xxx and max_value)。使得数据在索引局部顺序是“对的”。如果采用这种方式仍然不走索引扫描,还可以进一步细化分段或者采用“逐条提取 批绑定”的方法。

9. 建立组合索引,但查询谓词并未使用组合索引的第一列,此处有一个index skip scan概念, 10、like '%liu' 百分号在前 11,not in ,not exist 可以尝试把not in 或者 not exsts改成左连接的方式(前提是有子查询,并且子查询有where条件)。

例如:

  /*  index(i circleiconmast_ix1)*/
    i.iconno,
    i.circleid,
    i.filepath,
    i.regdt,
    i.filepath || '/' || i.filename imgname,
    i.filepath || '/' || 'th_160_' || i.filename smallimgname,
    i.memberid,
    i.admchk status,
    i.admchk originalstatus,
    rownum rn
     from circleiconmast i
    where i.regdt between to_date('20120619', 'yyyymmdd') - 10000 and
      to_date('20120621', 'yyyymmdd')
      and not exists (
       select c.validflg
         from circlemast c
        where c.validflg in ('n', 'f')
        and i.circleid = c.circleid)      
      and i.admchk = 'n'

改成左连接:

select
/*  index(i circleiconmast_ix1)*/
i.iconno,
i.circleid,
i.filepath,
i.regdt,
i.filepath || '/' || i.filename imgname,
i.filepath || '/' || 'th_160_' || i.filename smallimgname,
i.memberid,
i.admchk status,
i.admchk originalstatus,
rownum rn
  from circleiconmast i, circlemast c
where i.regdt between to_date('20110620', 'yyyymmdd') and
       to_date('20120621', 'yyyymmdd')   1
   and c.validflg not in ('n', 'f')
   and i.circleid = c.circleid
   and i.admchk = 'n'
总结:oracle中有很多情况会导致index失效,并且走全表扫描的代价是相当大的,所以在写sql的时候一定要注意这个会使索引失效的情况,养成良好的习惯。
网站地图