Oracle中组合索引怎么用

60次阅读
没有评论

共计 6926 个字符,预计需要花费 18 分钟才能阅读完成。

这篇文章给大家分享的是有关 Oracle 中组合索引怎么用的内容。丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,一起跟随丸趣 TV 小编过来看看吧。

Oracle 中组合索引的使用

关键词:组合索引 前导列   基于规则的优化器(RBO)  基于成本的优化器(CBO)  提示(hint)

在 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)。

[@more@]

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

我们创建测试表 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 优化器作出更好的选择。

感谢各位的阅读!关于“Oracle 中组合索引怎么用”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-24发表,共计6926字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)