Oracle中组合索引的使用详解(转)

在Oracle中可以创建组合索引,即同时包含两个或两个以上列的索引。在组合索引的使用方面,Oracle有以下特点:

1、 当使用基于规则的优化器(RBO)时,只有当组合索引的前导列出现在SQL语句的where子句中时,才会使用到该索引;

2、 在使用Oracle9i之前的基于成本的优化器(CBO)时, 只有当组合索引的前导列出现在SQL语句的where子句中时,才可能会使用到该索引,这取决于优化器计算的使用索引的成本和使用全表扫描的成本,Oracle会自动选择成本低的访问路径(请见下面的测试1和测试2);

3、 从Oracle9i起,Oracle引入了一种新的索引扫描方式——索引跳跃扫描(index skip scan),这种扫描方式只有基于成本的优化器(CBO)才能使用。这样,当SQL语句的where子句中即使没有组合索引的前导列,并且索引跳跃扫描的成本低于其他扫描方式的成本时,Oracle就会使用该方式扫描组合索引(请见下面的测试3);

4、 Oracle优化器有时会做出错误的选择,因为它再“聪明”,也不如我们SQL语句编写人员更清楚表中数据的分布,在这种情况下,通过使用提示(hint),我们可以帮助Oracle优化器作出更好的选择(请见下面的测试4)。

关于以上情况,我们分别测试如下:

我们创建测试表T,该表的数据来源于Oracle的数据字典表all_objects,表T的结构如下:

SQL> desc t

名称 是否为空? 类型

----------------------------------------- -------- ---------------------

OWNER NOT NULL VARCHAR2(30)

OBJECT_NAME NOT NULL VARCHAR2(30)

SUBOBJECT_NAME VARCHAR2(30)

OBJECT_ID NOT NULL NUMBER

DATA_OBJECT_ID NUMBER

OBJECT_TYPE VARCHAR2(18)

CREATED NOT NULL DATE

LAST_DDL_TIME NOT NULL DATE

TIMESTAMP VARCHAR2(19)

STATUS VARCHAR2(7)

TEMPORARY VARCHAR2(1)

GENERATED VARCHAR2(1)

SECONDARY VARCHAR2(1)

表中的数据分布情况如下:

SQL> select object_type,count(*) from t group by object_type;

OBJECT_TYPE COUNT(*)

------------------ ----------

CONSUMER GROUP 20

EVALUATION CONTEXT 10

FUNCTION 360

INDEX 69

LIBRARY 20

LOB 20

OPERATOR 20

PACKAGE 1210

PROCEDURE 130

SYNONYM 16100

TABLE 180

TYPE 2750

VIEW 8600

已选择13行。

SQL> select count(*) from t;

COUNT(*)

----------

29489

我们在表T上创建如下索引并对其进行分析:

SQL> create index indx_t on t(object_type,object_name);

索引已创建。

SQL> ANALYZE TABLE T COMPUTE STATISTICS

2 FOR TABLE

3 FOR ALL INDEXES

4 FOR ALL INDEXED COLUMNS

5 /

表已分析。

现在让我们编写几条SQL语句来测试一下Oracle优化器对访问路径的选择:

测试1)

SQL> set autotrace traceonly

SQL> SELECT * FROM T WHERE OBJECT_TYPE=‘LOB‘;

已选择20行。

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=22 Card=20 Bytes=1740)

1 0 TABLE ACCESS (BY INDEX ROWID) OF ‘T‘ (Cost=22 Card=20 Bytes=1740)

2 1 INDEX (RANGE SCAN) OF ‘INDX_T‘ (NON-UNIQUE) (Cost=2 Card=20)

正如我们所期望的,由于使用了组合索引的前导列并且访问了表中的少量记录,Oracle明智地选择了索引扫描。那么,如果我们访问表中的大量数据时,Oracle会选择什么样的访问路径呢?请看下面的测试:

测试2)

SQL> SELECT * FROM T WHERE OBJECT_TYPE=‘SYNONYM‘;

已选择16100行。

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=38 Card=16100 Bytes=1400700)

1 0 TABLE ACCESS (FULL) OF ‘T‘ (Cost=38 Card=16100 Bytes=1400700)

Statistics

----------------------------------------------------------

0 recursive calls

0 db block gets

1438 consistent gets

13 physical reads

0 redo size

941307 bytes sent via SQL*Net to client

12306 bytes received via SQL*Net from client

1075 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

16100 rows processed

很明显,即使使用了组合索引的前导列,但是由于访问了表中的大量数据,Oracle选择了不使用索引而直接使用全表扫描,因为优化器认为全表扫描的成本更低,但事实是不是真的这样的?我们通过增加提示(hint)来强制它使用索引来看看:

SQL> SELECT/**//*+ INDEX (T INDX_T)*/ * FROM T WHERE OBJECT_TYPE=‘SYNONYM‘;

已选择16100行。

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=16180 Card=16100 Bytes=1400700)

1 0 TABLE ACCESS (BY INDEX ROWID) OF ‘T‘ (Cost=16180 Card=16100 Bytes=1400700)

2 1 INDEX (RANGE SCAN) OF ‘INDX_T‘ (NON-UNIQUE) (Cost=80 Card=16100)

Statistics

----------------------------------------------------------

0 recursive calls

0 db block gets

17253 consistent gets

16 physical reads

0 redo size

298734 bytes sent via SQL*Net to client

12306 bytes received via SQL*Net from client

1075 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

16100 rows processed

从以上结果可以看出,在访问大量数据的情况下,使用索引确实会导致更高的执行成本,这从statistics部分的逻辑读取数(consistent gets)就可以看出,使用索引导致的逻辑读取数是不使用索引导致的逻辑读的10倍还多。因此,Oracle明智地选择了全表扫描而不是索引扫描。

下面,让我们来看看where子句中没有索引前导列的情况:

测试3)

SQL> select * from t where object_name= ‘DEPT‘;

已选择10行。

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=29 Card=14 Bytes=1218)

1 0 TABLE ACCESS (BY INDEX ROWID) OF ‘T‘ (Cost=29 Card=14 Bytes=1218)

2 1 INDEX (SKIP SCAN) OF ‘INDX_T‘ (NON-UNIQUE) (Cost=14 Card=14)

Statistics

----------------------------------------------------------

0 recursive calls

0 db block gets

24 consistent gets

0 physical reads

0 redo size

1224 bytes sent via SQL*Net to client

503 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

10 rows processed

OK!由于只查询了10条数据,即使没有使用前导列,Oracle正确地选择了索引跳跃扫描。我们再来看看如果不使用索引跳跃扫描,该语句的成本:

SQL> select/**//*+ NO_INDEX(T INDX_T)*/ * from t where object_name= ‘DEPT‘;

已选择10行。

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=38 Card=14 Bytes=1218)

1 0 TABLE ACCESS (FULL) OF ‘T‘ (Cost=38 Card=14 Bytes=1218)

Statistics

----------------------------------------------------------

0 recursive calls

0 db block gets

375 consistent gets

17 physical reads

0 redo size

1224 bytes sent via SQL*Net to client

503 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

10 rows processed

正如我们所料,不使用索引所导致的逻辑读(375)确实比使用索引的逻辑读多(24),达到10倍以上。

继续我们的测试,现在我们来看看Oracle不选择使用索引的情况:

测试4)

SQL> select * from t where object_name LIKE ‘DE%‘;

已选择180行。

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=38 Card=37 Bytes=3219)

1 0 TABLE ACCESS (FULL) OF ‘T‘ (Cost=38 Card=37 Bytes=3219)

Statistics

----------------------------------------------------------

0 recursive calls

0 db block gets

386 consistent gets

16 physical reads

0 redo size

12614 bytes sent via SQL*Net to client

624 bytes received via SQL*Net from client

13 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

180 rows processed

这次只选择了180条数据,跟表T中总的数据量29489条相比,显然只是很小的一部分,但是Oracle还是选择了全表扫描,有386个逻辑读。这种情况下,如果我们强制使用索引,情况会怎样呢?

SQL> select/**//*+ INDEX(T INDX_T)*/ * from t where object_name LIKE ‘DE%‘;

已选择180行。

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=182 Card=37 Bytes=3219)

1 0 TABLE ACCESS (BY INDEX ROWID) OF ‘T‘ (Cost=182 Card=37 Bytes=3219)

2 1 INDEX (FULL SCAN) OF ‘INDX_T‘ (NON-UNIQUE) (Cost=144 Card=37)

Statistics

----------------------------------------------------------

0 recursive calls

0 db block gets

335 consistent gets

0 physical reads

0 redo size

4479 bytes sent via SQL*Net to client

624 bytes received via SQL*Net from client

13 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

180 rows processed

通过添加提示(hint),我们强制Oracle使用了索引扫描(index full scan),执行了335个逻辑读,比使用全表扫描的时候少了一些。

由此可见,Oracle优化器有时会做出错误的选择,因为它再“聪明”,也不如我们SQL语句编写人员更清楚表中数据的分布,在这种情况下,通过使用提示(hint),我们可以帮助Oracle优化器作出更好的选择。

时间: 2024-10-19 09:43:48

Oracle中组合索引的使用详解(转)的相关文章

Oracle中常用的to_Char用法详解

Oracle中常用的to_Char用法详解(有FMT的详细列表) The following are number examples for the to_char function. to_char(1210.73, '9999.9') would return '1210.7' to_char(1210.73, '9,999.99') would return '1,210.73' to_char(1210.73, '$9,999.00') would return '$1,210.73'

oracle中exp,imp的使用详解

基本语法和实例: 一.EXP:       有三种主要的方式(完全.用户.表) 1.完全:           EXP SYSTEM/MANAGER BUFFER=64000 FILE=C:\FULL.DMP FULL=Y           如果要执行完全导出,必须具有特殊的权限 2.用户模式:           EXP SONIC/SONIC    BUFFER=64000 FILE=C:\SONIC.DMP OWNER=SONIC           这样用户SONIC的所有对象被输出到

Oracle中的列转行例子详解

数据如下:name id张三 1,2,3 要求实现:name id张三 1张三 2张三 3 --创建临时表 create table tmp as(select '张三' name, '1,2,3' id from dual); --写法1 select name,regexp_substr(id,'[^,]+',1,level) as id from tmp connect by level <= regexp_count(id,',')+1 order by id --写法2: select

Oracle中rownum机制原理&amp;用法详解

首先我要先感谢小月导师(导师Q:2901583663) 我今年29岁,来自西北的一个小县城. 我习惯了穷苦的日子,在初中还没毕业的时候,我跟着亲戚去了广东深圳打工.刚到深圳的时候,我被眼前的高楼大厦惊呆了!原来大城市是这样的繁华!我从那一刻开始,心里面下了一个决定:一定要出人头地,不能被人看不起. 我先是去了一家茶餐厅打工,那时候茶餐厅都是土豪喝的,看着他们西装革履,气质文雅,我打心里有点敬畏他们,所以对待客人的时候我都是毕恭毕敬.让我在服务员生涯中印象最深的一次是,那是一个冬天天气很冷,我穿的

Oracle中的索引详解

Oracle中的索引概述 索引与表一样,也属于段(segment)的一种.里面存放了用户的数据,跟表一样需要占用磁盘空间.索引是一种允许直接访问数据表中某一数据行的树型结构,为了提高查询效率而引入,是一个独立于表的对象,可以存放在与表不同的表空间中.索引记录中存有索引关键字和指向表中数据的指针(地址).对索引进行的I/O操作比对表进行操作要少很多.索引一旦被建立就将被Oracle系统自动维护,查询语句中不用指定使用哪个索引. 从物理上说,索引通常可以分为:分区和非分区索引.常规B树索引.位图(b

Mysql数据库的索引和视图详解

Mysql数据库的索引和视图详解 索引的概念 数据库的索引与书籍中的目录类似在一本书中,无需阅读整本书,利用目录就可以快速查找所需信息书中的目录是一个词语列表,其中注明了包含各个词的页码数据库索引在数据库中,索引数据库程序无需对整个表进行扫描,就可以在其中找到所需数据数据库中的索引是某个表中一列或若干列的集合,以及物理标识这些值的数据页的逻辑指针清单 索引的作用 设置了合适的索引之后,数据库利用葛总快速的定位技术,能够大大加快查询速率特别是当表很大时,或者查询涉及到多个表时,使用索引可使查询加快

【转载】lucene中Field.Index,Field.Store详解

lucene在doc.add(new Field("content",curArt.getContent(),Field.Store.NO,Field.Index.TOKENIZED)); Field有两个属性可选:存储和索引. 通过存储属性你可以控制是否对这个Field进行存储: 通过索引属性你可以控制是否对该Field进行索引. 事实上对这两个属性的正确组合很重要. Field.Index Field.Store 说明 TOKENIZED(分词) YES 被分词索引且存储 TOKE

Oracle中用exp/imp命令参数详解【转】

Oracle中用exp/imp命令参数详解 [用 exp 数 据 导 出]:1  将数据库TEST完全导出,用户名system 密码manager 导出到D:\daochu.dmp中   exp system/[email protected]   rows=y  indexes=y compress=n buffer=65536   feedback=100000full=y  file=d:\daochu.dmp  log=d:\daochulog.txt   owner=(ECC_BIZ,

Android总结篇系列:Activity中几个主要函数详解

专注Android领域开发. 仰望星空,同时需要脚踏实地. ——好记性不如烂博客 Android总结篇系列:Activity中几个主要函数详解 Activity作为Android系统中四大基本组件之一,包含大量的与其他的各大组件.intent.widget以及系统各项服务等之间的交互的函数.在此,本文主要选取实际项目开发中常用的,但完全理解又需要有一定深入了解的几个函数进行讲解,后续本文会根据需要不断更新. 1. startActivityForResult / onActivityResult