`
zhanglu0223
  • 浏览: 21627 次
  • 性别: Icon_minigender_1
  • 来自: 合肥
社区版块
存档分类
最新评论

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'; -- 转载修改,觉得原文作者使用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优化器作出更好的选择。

分享到:
评论

相关推荐

    详解oracle 10g的分区

    包括表分区方法、索引分区方法。 范围分区(range partitioning); 哈希分区(hash partitioning); 列表分区(list partitioning); 范围-哈希组合分区(composite range-hash partitioning); 范围-列表组合...

    Oracle 簇的使用详解

    簇其实就是一组表,由一组共享相同数据块的多个表组成,将经常一起使用的表组合在一起成簇可以提高处理效率;在一个簇中的表就叫做簇表。建立顺序是:簇→簇表→簇索引→数据创建簇的格式CREATE CLUSTER cluster_...

    精通SQL 结构化查询语言详解

    10.3.3 在HAVING子句中使用相关子查询  10.4 嵌套子查询  10.5 使用子查询创建视图  10.6 树查询 第11章 数据插入操作  11.1 插入单行记录  11.1.1 基本语法  11.1.2 整行插入  11.1.3 NULL值的...

    TianleSoftware Oracle中文学习手册

    1.2 索引详解 ........................................................................................................... 1.2.1 索引介绍 ...................................................................

    非常全的oracle文档

    五、 Oracle术语解释详解(摘自网络) 20 4.1. 数据库名 21 4.2. 数据库实例名 23 4.3. 数据库域名 24 4.4. 数据库服务名 24 六、 SYS和SYSTEM用户 25 七、 启动Oracle 25 八、 Oracle管理工具介绍 26 九、 查看Oracle...

    oracle学习经典教程

    1.2 索引详解..................28 1.2.1 索引介绍.........28 1.2.1.1 索引的创建语法......28 1.2.1.2 索引特点..................28 1.2.1.3 索引不足..................29 1.2.1.4 应该建索引列...

    精通SQL--结构化查询语言详解

    10.3.3 在having子句中使用相关子查询 201 10.4 嵌套子查询 203 10.5 使用子查询创建视图 204 10.6 树查询 205 第11章 数据插入操作 209 11.1 插入单行记录 209 11.1.1 基本语法 209 11.1.2 整行插入 209 ...

    asp.net知识库

    在ASP.NET中使用WINDOWS验证方式连接SQL SERVER数据库 改进ADO.Net数据库访问方式 ASP.NET 2.0 绑定高级技巧 简单实用的DataSet更新数据库的类+总结 [ADO.NET]由数据库触发器引发的问题 为ASP.NET封装的SQL数据库...

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.1.1. @Configurable object的单元测试 6.8.1.2. 多application context情况下的处理 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来...

    收获不止SQL优化

    8.3.2 组合索引的经典案例 231 8.4 本章习题、总结与延伸 234 第9章 且慢,弄清索引之阻碍让SQL飞 235 9.1 索引的不足之处 235 9.1.1 索引的各种开销 236 9.1.2 索引使用失效 236 9.2 感受美好索引另一面 237...

    亮剑.NET深入体验与实战精要2

    13.6.1 优先使用(对象)组合,而非(类)继承 478 13.6.2 针对接口编程,而非(接口的)实现 481 13.6.3 开放-封闭法则(OCP) 482 13.6.4 Liskov替换法则(LSP) 485 13.6.5 单一职责原则(SRP) 486 13.6.6 依赖...

    亮剑.NET深入体验与实战精要3

    13.6.1 优先使用(对象)组合,而非(类)继承 478 13.6.2 针对接口编程,而非(接口的)实现 481 13.6.3 开放-封闭法则(OCP) 482 13.6.4 Liskov替换法则(LSP) 485 13.6.5 单一职责原则(SRP) 486 13.6.6 依赖...

Global site tag (gtag.js) - Google Analytics