oracle查询不走索引的一些状况(索引失效)

Oracle创建索引的目的是为了不全表扫描,提升查询的效率。程序员

可是有些状况下,即便创建了索引,可是执行写出来的查询仍是很慢,而后经过执行计划会发现是索引失效致使的(不走索引,走全表扫描)。因此须要了解一下有哪些些状况会致使索引失效,即查询不走索引的缘由。函数

在写SQL的层面上一些骚操做会致使索引失效优化

没有写WHERE子句或查询条件没有创建索引spa

既然没有WHERE子句,那么就是查询所有数据了,至关于全表扫描,固然不走索引了。code

而查询条件上没有创建索引的话,索引都没有还走个毛索引啊。blog

WHERE子句上没有使用索引中的引导列索引

要使用索引,则查询条件中必须包含索引中的引导列。好比一个复合索引包含A,B,C,D四列,则A为引导列(排在第一位置的列)。若是WHERE子句中所包含的列是BCD或者BD等状况,则只能使用非匹配索引扫描。class

-- 建立包含A,B,C,D四列的复合索引
CREATE INDEX INDEX_ABCD ON LETTERS(A, B, C, D);
-- 下列语句不会使用复合索引
SELECT * FROM LETTERS WHERE B = 'b' AND C = 'c'; SELECT * FROM LETTERS WHERE B = 'b' AND D = 'd'; SELECT * FROM LETTERS WHERE C = 'c' AND D = 'd'; SELECT * FROM LETTERS WHERE B = 'b' AND C = 'c' AND D = 'd';

另外,单独引用复合索引里排第一位置的索引列也会致使索引失效,复合索引必须复合使用才能生效。效率

-- 单独使用复合索引中的引导列也不会触发复合索引
SELECT * FROM LETTERS WHERE A = 'a'

WHERE子句中使用IS NULL或IS NOT NULL数据类型

使用判断空或非空的条件会致使该索引列失效。

-- COMM列的索引会失效
SELECT * FROM EMP WHERE COMM IS NULL;

WHERE子句中使用函数

若是没有使用基于函数的索引,那么WHERE子句中对存在索引的列使用函数时,会使优化器忽略这些索引。

-- 在没有创建基于函数索引的字段上使用了函数,会致使索引失效
SELECT * FROM STUFF WHERE TRUNC(BIRTHDAY) = '2019-04-01'

非要这样的话能够经过在索引列上创建基于函数的索引来解决问题。

-- 给BIRTHDAY列建立TRUNC函数的函数索引
CREATE INDEX STUFF_BIRTHDAY_FBI_IDX ON STUFF(TRUNC(BIRTHDAY));

这样的话函数索引就起做用了,也就能在索引列上使用函数了,并且这时候你想不用函数都不行。

可是对于MIN,MAX函数等,Oracle仍然会使用索引,由于Oracle对这类聚合分析函数作了相应的优化。

对索引列进行运算致使索引失效

和使用函数一样的,若是使用运算,好比加减乘除非等,也会致使索引失效。

-- 对索引列进行了减法运算,致使不走索引
SELECT * FROM USERS WHERE AGE - 1 = 23

-- 正确的写法(去除运算)
SELECT * FROM USERS WHERE AGE = 24

使用前导模糊查询

前导模糊查询的写法是LIKE '%T',即通配符【%】写在要匹配的字符前面,这样的状况下不走索引。

-- 前导模糊查询不走索引
SELECT * FROM LOVERS WHERE NAME LIKE '%静';

WHERE子句中使用不等于条件

不等于操做包括:

<>
!=
NOT COL >= ? NOT COL <= ?

为何不等于条件会不走索引,咱们能够用常规的思惟去推断一下。首先【不等于】这个概念能够推断成选取结果集中的一个比较大的部分,好比咱们要找出中国人口中不是姓杨的,要找出资深程序员中不会秃头的,均可以认为是要返回结果集中一个很大的部分。由于Oracle的CBO是闭源的,咱们只能推断Oracle会认为既然要返回结果集中很大的一个部分,不如直接使用全表扫描而不考虑索引。

并且Oracle是建议,对于这些限制条件可使用OR代替,例如COL <> 0能够替换成COL > 0 OR COL < 0。

WHERE子句中使用NOT IN或NOT EXISTS

使用NOT IN或NOT EXISTS也会致使不走索引。

事实上NOT IN和NOT EXISTS也能够看作是不等于条件,不走索引的缘由和上面的不等于条件相同。

另外有一些人说使用IN是不走索引的,这是不对的,IN是能够走索引的,只是可能效率会比EXISTS低。

等于和范围索引不会被合并使用

SELECT * FROM CARS WHERE COLOR = 'yellow' AND TYPE = 'B'

在上面的查询中,COLOR和TYPE列上都建立了非惟一索引,在这种查询条件下Oracle并不会合并索引,而只会使用第一个索引,即只有COLOR列上索引会生效。

比较不匹配类型的数据类型

好比SERIAL_CODE是一个VARCHAR2类型的字段,在这个字段上创建了索引,可是下面的语句将会执行全表扫描而不走索引。

SELECT * FROM MATERIALS WHERE SERIAL_CODE = 810646874;

为何呢?由于Oracle中有一个字段自动进行隐式类型转换的机制,会自动把类型不匹配的字段进行隐式类型转换,至关于:

SELECT * FROM MATERIALS WHERE TO_NUMBER(SERIAL_CODE) = 810646874;

能够看出,Oracle优化器自动给SERIAL_CODE加上了类型转换函数,这样就限制了索引的使用。

因此正确的写法应该是条件和字段类型相匹配:

SELECT * FROM MATERIALS WHERE SERIAL_CODE = '810646874';

表中数据量的多少也会影响索引的使用

查询的数量是大表的大部分,应该是30%以上

若是查询的数量超过大表数量的30%,那就不走索引了。

对小表查询

举个极端的例子,表中只有一条数据,何须走索引呢。好比你看一本只有几页的书,难道你还会去看目录吗,给这本书建目录都是人才了,你还去找这本书有没有目录岂不是人才中的人才(你别去上班了,我建目录养你啊)。

索引失效的解决办法

下面这些解决办法是基于SQL写得没问题,而索引就是不生效的状况。

选用合适的Oracle优化器

Oracle的优化器有三种,一种是基于规则的(RULE),一种是基于成本的(COST),还有一种是选择性的(CHOOSE)。

在缺省的状况下(未设置),Oracle默认采用CHOOSE优化器。为了不那些没必要要的全表扫描(FULL TABLE SCAN),你必须尽可能避免使用CHOOSE优化器,而直接采用基于规则或基于成本的优化器。

重建索引

和电脑出了问题的重启试试同样,索引出了问题也是能够重建试试的。

ALTER INDEX 索引名 REBUILD

强制索引

强制索引是使用hint关键字。

正常使用的索引忽然失效的对应解决办法

有些时候,一样的SQL,以前是能走索引的,忽然有一天不走索引了,那么多是:

1.随着表的增加,WHERE条件出来的数据太多,大于15%,致使CBO计算出走索引扫描大于走全表扫描,就会使得索引失效。这种状况目前我也不知道该怎么办(好难啊)。

2.统计信息失效。这种状况须要从新搜集统计信息。

3.索引自己失效。这种状况就很玄乎了,只能重建索引试试。 

 

"人始终要走到某个路口,看着一路的同伴有些往左走,有些往右走,只剩下本身一个左顾右盼,不知所措。要么跟着大部队往左往右,要么就朝前走,绝没有任何退后的可能。"

相关文章
相关标签/搜索