SQL> explain plan for select empno, ename from emp where empno=10; Query Plan ------------------------------------ SELECT STATEMENT [CHOOSE] Cost=1 TABLE ACCESS BY ROWID EMP [ANALYZED] INDEX UNIQUE SCAN EMP_I1
但是如果查询的数据能全在索引中找到,就可以避免进行第2步操作,避免了不必要的I/O,此时即使通过索引扫描取出的数据比较多,效率还是很高的 SQL> explain plan for select empno from emp where empno=10;-- 只查询empno列值 Query Plan ------------------------------------ SELECT STATEMENT [CHOOSE] Cost=1 INDEX UNIQUE SCAN EMP_I1
进一步讲,如果sql语句中对索引列进行排序,因为索引已经预先排序好了,所以在执行计划中不需要再对索引列进行排序 SQL> explain plan for select empno, ename from emp where empno > 7876 order by empno; Query Plan -------------------------------------------------------------------------------- SELECT STATEMENT[CHOOSE] Cost=1 TABLE ACCESS BY ROWID EMP [ANALYZED] INDEX RANGE SCAN EMP_I1 [ANALYZED]
从这个例子中可以看到:因为索引是已经排序了的,所以将按照索引的顺序查询出符合条件的行,因此避免了进一步排序操作。 根据索引的类型与where限制条件的不同,有4种类型的索引扫描: 索引唯一扫描(index unique scan) 索引范围扫描(index range scan) 索引全扫描(index full scan) 索引快速扫描(index fast full scan)
(1) 索引唯一扫描(index unique scan) 通过唯一索引查找一个数值经常返回单个ROWID.如果存在UNIQUE 或PRIMARY KEY 约束(它保证了语句只存取单行)的话,Oracle经常实现唯一性扫描。 使用唯一性约束的例子: SQL> explain plan for select empno,ename from emp where empno=10; Query Plan ------------------------------------ SELECT STATEMENT [CHOOSE] Cost=1 TABLE ACCESS BY ROWID EMP [ANALYZED] INDEX UNIQUE SCAN EMP_I1
(2) 索引范围扫描(index range scan) 使用一个索引存取多行数据,在唯一索引上使用索引范围扫描的典型情况下是在谓词(where限制条件)中使用了范围操作符(如>、<、<>、>=、<=、between) 使用索引范围扫描的例子: SQL> explain plan for select empno,ename from emp where empno > 7876 order by empno; Query Plan -------------------------------------------------------------------------------- SELECT STATEMENT[CHOOSE] Cost=1 TABLE ACCESS BY ROWID EMP [ANALYZED] INDEX RANGE SCAN EMP_I1 [ANALYZED]
(3) 索引全扫描(index full scan) 与全表扫描对应,也有相应的全索引扫描。而且此时查询出的数据都必须从索引中可以直接得到。 全索引扫描的例子: An Index full scan will not perform single block i/o^^s and so it may prove to be inefficient. e.g. Index BE_IX is a concatenated index on big_emp (empno, ename) SQL> explain plan for select empno, ename from big_emp order by empno,ename; Query Plan -------------------------------------------------------------------------------- SELECT STATEMENT[CHOOSE] Cost=26 INDEX FULL SCAN BE_IX [ANALYZED]
(4) 索引快速扫描(index fast full scan) 扫描索引中的所有的数据块,与 index full scan很类似,但是一个显著的区别就是它不对查询出的数据进行排序,即数据不是以排序顺序被返回。在这种存取方法中,可以使用多块读功能,也可以使用并行读入,以便获得最大吞吐量与缩短执行时间。 索引快速扫描的例子: BE_IX索引是一个多列索引: big_emp (empno,ename) SQL> explain plan for select empno,ename from big_emp; Query Plan ------------------------------------------ SELECT STATEMENT[CHOOSE] Cost=1 INDEX FAST FULL SCAN BE_IX [ANALYZED]
只选择多列索引的第2列: SQL> explain plan for select ename from big_emp; Query Plan ------------------------------------------ SELECT STATEMENT[CHOOSE] Cost=1 INDEX FAST FULL SCAN BE_IX [ANALYZED]
SQL> explain plan for select /*+ ordered */ e.deptno, d.deptno from emp e, dept d where e.deptno = d.deptno order by e.deptno, d.deptno; Query Plan ------------------------------------- SELECT STATEMENT [CHOOSE] Cost=17 MERGE JOIN SORT JOIN TABLE ACCESS FULL EMP [ANALYZED] SORT JOIN TABLE ACCESS FULL DEPT [ANALYZED]
HASH连接的例子: SQL> explain plan for select /*+ use_hash(emp) */ empno from emp, dept where emp.deptno = dept.deptno; Query Plan ---------------------------- SELECT STATEMENT[CHOOSE] Cost=3 HASH JOIN TABLE ACCESS FULL DEPT TABLE ACCESS FULL EMP
Full table scans, FTS(全表扫描):通过设置db_block_multiblock_read_count可以设置一次IO能读取的数据块个数,从而有效减少全表扫描时的IO总次数,也就是通过预读机制将将要访问的数据块预先读入内存中。只有在全表扫描情况下才能使用多块读操作。 Table Access by rowed(通过rowid存取表,rowid lookup):由于rowid中记录了行存储的位置,所以这是oracle存取单行数据的最快方法。 Index scan(索引扫描index lookup):在索引中,除了存储每个索引的值外,索引还存储具有此值的行对应的rowid值,索引扫描分两步1,扫描索引得到rowid;2,通过 rowid读取具体数据。每步都是单独的一次IO,所以如果数据经限制条件过滤后的总量大于原表总行数的5%-10%,则使用索引扫描效率下降很多。而如果结果数据能够全部在索引中找到,则可以避免第二步操作,从而加快检索速度。 根据索引类型与where限制条件的不同,有4种类型的索引扫描: Index unique scan(索引唯一扫描):存在unique或者primary key的情况下,返回单个rowid数据内容。 Index range scan(索引范围扫描):1,在唯一索引上使用了range操作符(>,<,<>,>=,<=,between);2,在组合索引上,只使用部分列进行查询;3,对非唯一索引上的列进行的查询。 Index full scan(索引全扫描):需要查询的数据从索引中可以全部得到。 Index fast full scan(索引快速扫描):与index full scan类似,但是这种方式下不对结果进行排序。