sql中索引不会被⽤到的⼏种情况
转载⾃
1、查询谓词没有使⽤索引的主要边界,换句话说就是select *,可能会导致不⾛索引。
⽐如,你查询的是SELECT * FROM T WHERE Y=XXX;假如你的T表上有⼀个包含Y值的组合索引,但是优化器会认为需要⼀⾏⾏的扫描会更有效,这个时候,优化器可能会选择TABLE ACCESS FULL,但是如果换成了SELECT Y FROM T WHERE Y = XXX,优化器会直接去索引中到Y的值,因为从B树中就可以到相应的值。
sql优化的几种方式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条件)。
例如:
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'
改成左连接:
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的时候⼀定要注意这个会使索引失效的情况,养成良好的习惯。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论