通常,通过索引查询数据比全表扫描要快

使用索引能提高查询效率,但是我们也必须注意到它的代价。索引需要空间来存储,也需要定期维护,每当有记录在表中增减或索引列被修改时,索引本身也会被修改。

这意味着每条记录的INSERT、DELETE、UPDATE将为此付出更多的磁盘I/O。因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。

过度索引

一般情况下,使用索引可以缩短查询语句的执行时间,提高系统的执行效率,但是要避免以下两种过度索引的情况出现: 对一个表建立了过多的索引,从而造成维护索引所需要的时间超过使用索引所降低的时间,从而造成整个系统效率的下降,这一般发生在对一些进行大量更新的表上面。因此一个联机表上的索引,最多不要超过5个。

由于索引数据的区分度不够,造成了使用索引而引起的效率的下降,这一般发生在对数据进行大的统计分析索引唯一扫描 ( INDEX UNIQUE SCAN)的时候。可以通过指定全表扫描等提示(hint)来避免。

ORACLE对索引有两种访问模式

索引唯一扫描 ( INDEX UNIQUE SCAN)

例如: 表REQUEST_LOG有两个索引:建立在REQUESTID列上的唯一性索引REQUESTID_PK和建立在REMARK列上的非唯一性索引IDX$REMARK。 SELECT * FROM REQUEST_LOG WHERE REQUESTID = '123456'; 在ORACLE内部,上述SQL将被分成两步执行,首先,REQUESTID_PK索引将通过索引唯一扫描的方式被访问,获得相对应的ROWID,通过ROWID访问表的方式执行下一步检索。如果被检索返回的列包括在INDEX列中,ORACLE将不执行第二步的处理(通过ROWID访问表)。因为检索数据保存在索引中, 单单访问索引就可以完全满足查询结果。

索引范围查询(INDEX RANGE SCAN)

1. 基于一个范围的检索 SELECT * FROM REQUEST_LOG WHERE REQUESTID  LIKE '123%'; WHERE子句条件包括一系列值,ORACLE将通过索引范围查询的方式查询REQUESTID_PK。由于索引范围查询将返回一组值,它的效率就要比索引唯一扫描低一些。

2. 基于非唯一性索引的检索 SELECT * FROM REQUEST_LOG WHERE REMARK = ‘APPROVED'; 这个SQL的执行分两步,REMARK的索引范围查询(得到所有符合条件记录的ROWID) 和下一步同过ROWID访问表得到列的值。由于REMARK是一个非唯一性的索引,数据库不能对它执行索引唯一扫描。由于SQL返回列,而它并不存在于REMARK索引中,所以在索引范围查询后会执行一个通过ROWID访问表的操作。

如何命中索引

避免在索引列上使用计算

WHERE子句中,如果索引列是函数的一部分。优化器将不使用索引而使用全表扫描。

低效SQL:
高效SQL: SELECT … FROM DEPT WHERE SAL > 25000/12;
用>=替代>
低效SQL: SELECT * FROM EMP WHERE DEPTNO >3;
高效SQL: SELECT * FROM EMP WHERE DEPTNO >=4;

两者的区别在于,前者DBMS将直接跳到第一个DEPT等于4的记录而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录

避免在索引列上使用IS NULL和IS NOT NULL

避免在索引中使用任何可以为空的列,ORACLE将无法使用该索引。对于单列索引,如果列包含空值,索引中将不存在此记录。对于复合索引,如果每个列都为空,索引中同样不存在此记录。

如果至少有一个列不为空,则记录存在于索引中。

总是使用索引的第一个列

如果索引是建立在多个列上,只有在它的第一个列(leadingcolumn)被where子句引用时,优化器才会选择使用该索引。当引用索引的其他列时,优化器使用了全表扫描而忽略了索引。