强制索引的方法

一般来说强制索引都是采用/*+INDEX(表名,索引名字)*/ 的方法,但是这种方法有个缺点,如果索引的名字改变了,就会导致程序重新改动,大大增加维护成本。
 
其实索引提示还可以使用列的方法进行,语法是/*+INDEX(表名,(索引列的列表))*/,这种方法即使索引的名字变了,也不会导致程序重新改动,除非索引列的顺序变化了。
 
看如下一个简单例子:
 
SQL> create table test as select * from all_objects;
 
Table created.
 
SQL> create index idx_test_01 on test(object_name);
 
Index created.
 
SQL> create index idx_test_02 on test(owner,object_id);
 
Index created.
 
SQL> exec dbms_stats.gather_table_stats(user,'test',cascade=>true);
 
PL/SQL procedure successfully completed.

首先创建了一个表TEST,并且建立了2个索引,然后收集了统计信息。
 
SQL> select * from test where owner='SYS' and object_name='DBA_OBJECTS';
Execution Plan
----------------------------------------------------------
Plan hash value: 3384190782
-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |   238 | 26656 |    35   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TEST        |   238 | 26656 |    35   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST_01 |   952 |       |     6   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OWNER"='SYS')
   2 - access("OBJECT_NAME"='DBA_OBJECTS')

由于OBJECT_NAME的可选择性比OWNER好很多,因此执行计划选择了走索引IDX_TEST_01 。
下面我们采用强制索引提示让其走索引IDX_TEST_02,
 
SQL> select /*+index(test,idx_test_02)*/ * from test where owner='SYS' and
object_name='DBA_OBJECTS';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1715650972
-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |   238 | 26656 |  3284   (1)| 00:00:40 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TEST        |   238 | 26656 |  3284   (1)| 00:00:40 |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST_02 | 97844 |       |   326   (1)| 00:00:04 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_NAME"='DBA_OBJECTS')
   2 - access("OWNER"='SYS')

这种强制索引提示的方法是普遍采用的方法,但是这种方法在索引名字改变后,就会导致提示的失效。

SQL> alter index IDX_TEST_02 rename to IDX_TEST_03;
 
Index altered.
 
SQL> select /*+index(test,idx_test_02)*/ * from test where owner='SYS' and object_name='DBA_OBJECTS';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3384190782
-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |   238 | 26656 |    35   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TEST        |   238 | 26656 |    35   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST_01 |   952 |       |     6   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OWNER"='SYS')
   2 - access("OBJECT_NAME"='DBA_OBJECTS')

在把索引的名字idx_test_02重新命名为idx_test_03后,索引提示/*+index(test,idx_test_02)*/  已经失效了。

如果采用列的方法,索引名字的更改不会导致提示的失效。

SQL> select /*+index(test,(owner,object_id))*/ * from test where owner='SYS' and object_name='DBA_OBJECTS';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 883341653
-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |   238 | 26656 |  3284   (1)| 00:00:40 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TEST        |   238 | 26656 |  3284   (1)| 00:00:40 |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST_03 | 97844 |       |   326   (1)| 00:00:04 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_NAME"='DBA_OBJECTS')
   2 - access("OWNER"='SYS')
 
SQL> alter index idx_test_03 rename to idx_test_02;
 
Index altered.
 
SQL> select /*+index(test,(owner,object_id))*/ * from test where owner='SYS' and object_name='DBA_OBJECTS';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1715650972
-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |   238 | 26656 |  3284   (1)| 00:00:40 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TEST        |   238 | 26656 |  3284   (1)| 00:00:40 |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST_02 | 97844 |       |   326   (1)| 00:00:04 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_NAME"='DBA_OBJECTS')
   2 - access("OWNER"='SYS')
 
可以看到即使索引的名字改变了,我们还是能用到我们想要的索引。
 
使用索引列提示的时候要注意:必须把索引的前导列放在前面,在本例中采用前导列即使OWNER,如果采
用/*+index(test,(object_id,owner))*/  提示就不起作用。
 
SQL> select /*+index(test,(object_id,owner))*/  * from test where owner='SYS' and object_name='DBA_OBJECTS';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3384190782
-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |   238 | 26656 |    35   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TEST        |   238 | 26656 |    35   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST_01 |   952 |       |     6   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OWNER"='SYS')
   2 - access("OBJECT_NAME"='DBA_OBJECTS')

在某些情况下,只带前导列即可。
 
SQL> select /*+index(test,(owner))*/ * from test where owner='SYS' and object_name='DBA_OBJECTS';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1715650972
-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |   238 | 26656 |  3284   (1)| 00:00:40 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TEST        |   238 | 26656 |  3284   (1)| 00:00:40 |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST_02 | 97844 |       |   326   (1)| 00:00:04 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_NAME"='DBA_OBJECTS')
   2 - access("OWNER"='SYS')

如果创建索引列的顺序变了,就将会导致提示的失效(如果采用索引名提示可能会失效也可能不会失效):
 
SQL> drop index idx_test_02;
 
Index dropped.
 
SQL> create index idx_test_02 on test(object_id,owner);
 
Index created.
 
SQL> select /*+index(test,(owner,object_id))*/ * from test where owner='SYS' and object_name='DBA_OBJECTS';
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3384190782
-------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |   238 | 26656 |    35   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TEST        |   238 | 26656 |    35   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST_01 |   952 |       |     6   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OWNER"='SYS')
   2 - access("OBJECT_NAME"='DBA_OBJECTS')
SQL>

因此,如果索引的名字经常改变,在编码的时候尽量采用索引列提示的方法。如果索引的列顺序经常改变,在编码的时候尽量采用索引名提示的方法。
 
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当Oracle的强制索引没有效果时,可能有以下几个原因: 1. 索引选择不当:强制索引是通过强制查询优化器使用某个索引来执行查询。但是如果选择的索引不合适,可能会导致效果不佳。索引的选择应该根据查询的条件和表的大小来进行。 2. 统计信息不准确:查询优化器使用统计信息来估计查询的成本和选择最合适的执行计划。如果统计信息不准确或者过时,就会导致查询优化器选择错误的索引或执行计划。 3. 强制索引被覆盖:如果强制索引被其他索引所覆盖,那么查询优化器可能会选择覆盖索引来执行查询,而忽略强制索引。 4. 数据冗余:如果表中的数据冗余较多,那么强制索引可能无法提供明显的性能提升效果。 解决这些问题的方法有: 1. 确保选择合适的索引:根据查询的条件和表的大小,选择合适的索引。通过使用索引提示或者修改查询语句来强制使用正确的索引。 2. 更新统计信息:定期更新表的统计信息,使得查询优化器能够选择正确的索引和执行计划。 3. 检查是否有覆盖索引:使用explain plan等工具来检查查询的执行计划,确保强制索引没有被其他索引所覆盖。 4. 优化数据结构:如果表中的数据冗余较多,考虑对表的数据进行优化,减少冗余数据的存储。 总而言之,当Oracle的强制索引没有效果时,可以通过选择合适的索引、更新统计信息、检查覆盖索引和优化数据结构来解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值