在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优化器作出更好的选择。
分享到:
相关推荐
- **问题描述**:当WHERE子句中使用`<>`或`!=`等不等于操作符时,即使被比较的列有索引,Oracle也可能执行全表扫描。 - **解决方案**:将`<>`操作符转换为两个条件`和`>`的组合,这样在使用基于规则的优化器时,...
Oracle 执行计划详解是数据库管理系统中一个非常重要的概念。本文将详细介绍 Oracle 执行计划的相关概念、访问数据的存取方法、表之间的连接等内容,并对总结和概述,以便于理解和记忆。 一、相关的概念 1. Rowid ...
Oracle分区技术是数据库管理系统中用于优化大规模数据存储和查询性能的一种高级特性。它允许将大表和索引分成较小、更易管理和操作的部分,每个部分称为一个分区。分区的主要目标是提高查询性能、简化管理任务并增强...
Oracle EXPIMP工具是Oracle数据库系统中用于数据迁移和备份恢复的重要工具。虽然随着技术的发展,RMAN和其他第三方工具已经成为了大型数据库备份的主要选择,但在处理小型数据库、表空间迁移、表抽取以及解决逻辑和...
Oracle 的 Parallel 并行技术案例详解 Oracle 的 Parallel 并行技术是指在数据库系统中实现并行处理的技术,通过将一个大的任务分解成多个小的任务,并利用多个进程或线程来并发执行这些小任务,从而提高系统的处理...
### Oracle 分区表与分区索引详解 #### 一、Oracle分区概述 在Oracle数据库中,分区技术是一种非常有效的管理大型表和索引的方法。通过将一个大的表或索引分成多个较小的部分(分区),可以显著提高查询性能,并...
### Oracle SQLPLUS命令详解 #### 一、Oracle的启动与关闭 在操作Oracle数据库时,启动和关闭系统是基础操作之一。以下将详细介绍如何通过Oracle的`sqlplus`工具及`svrmgrl`来执行这些操作。 ##### 启动Oracle...
### Oracle索引详解 #### 一、索引介绍 ##### 1.1 索引的创建语法 在Oracle数据库中,索引是一种用于提高数据检索速度的数据结构。索引的创建语法通常遵循以下格式: ```sql CREATE [UNIQUE | BITMAP] INDEX ...
### Oracle强制索引详解 在Oracle数据库管理过程中,优化查询性能是提高系统效率的关键环节之一。其中,**强制索引**是一种重要的优化手段,它允许数据库管理员或开发人员指定特定的执行计划,确保查询能够利用预设...
在Oracle数据库中,索引是一种优化查询性能的关键工具。它们加快了数据检索速度,使得数据库管理系统(DBMS)能够更快地定位到所需的数据行。本文将深入探讨Oracle索引的类型、结构以及如何创建和使用。 首先,让...
要了解`USER_INDEXES`表中各字段的含义,可以使用: ```sql SELECT column_name, comments FROM dict_columns WHERE table_name = 'USER_INDEXES'; ``` 此外,还有很多其他常用的数据字典查询,如查询用户信息(`...
### 详解 Oracle 10g 的分区技术 #### 一、分区概述 在数据库管理领域,**分区**是一种重要的优化手段,它通过将一个大型表或索引分割成多个较小的、更容易管理的部分来提高数据库的性能和管理效率。虽然在应用...
14. 避免在WHERE子句中使用函数 函数通常会导致无法使用索引,除非它是索引表达式的一部分。 15. 使用物化视图 物化视图可以预先计算并存储结果,加速查询速度。 16. 优化GROUP BY和ORDER BY 合理使用索引和分区,...
组合索引是指在一个索引中包含多个列的索引。组合索引可以同时支持多个字段的排序,但需要注意字段顺序的选择,以确保索引的有效使用。 **1.2.1.9 ORACLE ROWID** ROWID是Oracle中一种特殊的数据类型,表示数据库...
### MySQL数据库及索引详解 #### 一、MySQL简介与数据库发展 MySQL是一款非常流行的开源关系型数据库管理系统,由瑞典MySQL AB公司开发。它以其高性能、稳定性和易用性著称,广泛应用于Web应用程序和企业级系统中...
- 精心设计索引结构:考虑创建唯一索引、组合索引和位图索引,以适应不同的查询需求。避免过度索引,这可能导致写操作变慢。 - 使用函数索引需谨慎:函数索引可以加速特定类型的查询,但可能会影响插入和更新性能。 ...
- 在WHERE子句中使用函数可能会导致Oracle无法使用索引。 - **1.2.1.6.4 比较不匹配的数据类型** - 当比较的数据类型不一致时,Oracle可能无法使用索引。 **1.2.1.7 查询索引** 可以使用`DBA_INDEXES`或`USER_...