Oracle中的Hint可以用来调整SQL的执行计划,提高SQL执行效率。下面分类介绍Oracle数据库中常见的Hint。这里描述的是Oracle11gR2中的常见Hint,Oracle数据库中各个版本中的Hint都不尽相同,所以这里讲述的的Hint可能并不适用于Oracle早期的版本。

一、与优化器模式相关的Hint

1、ALL_ROWS

ALL_ROWS是针对整个目标SQL的Hint,它的含义是让优化器启用CBO,而且在得到目标SQL的执行计划时会选择那些吞吐量最佳的执行路径。这里的“吞吐量最佳”是指资源消耗量(即对I/O、CPU等硬件资源的消耗量)最小,也就是说在ALL_ROWS Hint生效的情况下,优化器会启用CBO而且会依据各个执行路径的资源消耗量来计算它们各自的成本。

ALL_ROWS Hint的格式如下:

/*+ ALL_ROWS */

使用范例:

1
2
3
select /*+ all_rows */ empno,ename,sal,job
  from emp
 where empno=7396;

从Oracle10g开始,ALL_ROWS就是默认的优化器模式,启用的就是CBO。

1
2
3
4
5
scott@TEST>show parameter optimizer_mode
 
NAME               
     TYPE         
       VALUE
------------------------------------ --------------------------------- ------------------------------
optimizer_mode         
     string                   ALL_ROWS

如果目标SQL中除了ALL_ROWS之外还使用了其他与执行路径、表连接相关的Hint,优化器会优先考虑ALL_ROWS。

2、FIRST_ROWS(n)

FIRST_ROWS(n)是针对整个目标SQL的Hint,它的含义是让优化器启用CBO模式,而且在得到目标SQL的执行计划时会选择那些能以最快的响应时间返回头n条记录的执行路径,也就是说在FIRST_ROWS(n) Hint生效的情况下,优化器会启用CBO,而且会依据返回头n条记录的响应时间来决定目标SQL的执行计划。

FIRST_ROWS(n)格式如下:

/*+ FIRST_ROWS(n) */

使用范例

1
2
3
select /*+ first_rows(10) */ empno,ename,sal,job
  from emp
 where deptno=30;

上述SQL中使用了/*+ first_rows(10) */,其含义是告诉优化器我们想以最短的响应时间返回满足条件"deptno=30"的前10条记录。

注意,FIRST_ROWS(n) Hint和优化器模式FIRST_ROWS_n不是一一对应的。优化器模式FIRST_ROWS_n中的n只能是1、10、100、1000。但FIRST_ROWS(n) Hint中的n还可以是其他值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
scott@TEST>alter session set optimizer_mode=first_rows_9;
ERROR:
ORA-00096: invalid value FIRST_ROWS_9 for parameter optimizer_mode, must be from among first_rows_1000, first_rows_100, first_rows_10, first_rows_1, first_rows, all_rows, choose,rule
 
scott@TEST>set autotrace traceonly
scott@TEST>select /*+ first_rows(9) */ empno from emp;
 
14 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 179099197
 
---------------------------------------------------------------------------
| Id  | Operation  
 | 
Name   Rows  | Bytes | Cost (%CPU)| Time 
  |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT |  
  |   9 |    36 |   1   (0)| 00:00:01 |
|   1 |  INDEX FULL SCAN | PK_EMP | 
9 |    36 |   1   (0)| 00:00:01 |
---------------------------------------------------------------------------

如果在UPDATE、DELETE或者含如下内容的查询语句中使用了FIRST_ROWS(n) Hint,则该Hint会被忽略:

  • 集合运算(如UNION,INTERSACT,MINUS,UNION ALL等)

  • GROUP BY

  • FOR UPDATE

  • 聚合函数(比如SUM等)

  • DISTINCT

  • ORDER BY(对应的排序列上没有索引)

这里优化器会忽略FIRST_ROWS(n) Hint是因为对于上述类型的SQL而言,Oracle必须访问所有的行记录后才能返回满足条件的头n行记录,即在上述情况下,使用该Hint是没有意义的。

3、RULE

RULE是针对整个目标SQL的Hint,它表示对目标SQL启用RBO。

格式如下:

/*+ RULE */

使用范例:

1
2
3
select /*+ rule */ empno,ename,sal,job
  from emp
 where deptno=30;

RULE不能与除DRIVING_SITE以外的Hint联用,当RULE与除DRIVING_SITE以外的Hint联用时,其他Hint可能会失效;当RULE与DRIVING_SITE联用时,它自身可能会失效,所以RULE Hint最好是单独使用。

一般情况下,并不推荐使用RULE Hint。一来是因为Oracle早就不支持RBO了,二来启用RBO后优化器在执行目标SQL时可选择的执行路径将大大减少,很多执行路径RBO根本就不支持(比如哈希连接),就也就意味着启用RBO后目标SQL跑出正确执行计划的概率将大大降低。

因为很多执行路径RBO根本就不支持,所以即使在目标SQL中使用了RULE Hint,如果出现了如下这些情况(包括但不限于),RULE Hint依然会被Oracle忽略。

  • 目标SQL除RULE之外还联合使用了其他Hint(如DRIVING_SITE)。

  • 目标SQL使用了并行执行

  • 目标SQL所涉及的对象有IOT

  • 目标SQL所涉及的对象有分区表

......

二、与表访问相关的Hint

1、FULL

FULL是针对单个目标表的Hint,它的含义是让优化器对目标表执行全表扫描。

格式如下:

/*+ FULL(目标表) */

使用范例:

1
2
3
select /*+ full(emp) */ empno,ename,sal,job
  from emp
 where deptno=30;

上述SQL中Hint的含义是让优化器对目标表EMP执行全表扫描操作,而不考虑走表EMP上的任何索引(即使列EMPNO上有主键索引)。

2、ROIWD

ROIWD是针对单个目标表的Hint,它的含义是让优化器对目标表执行RWOID扫描。只有目标SQL中使用了含ROWID的where条件时ROWID Hint才有意义。

格式如下:

/*+ ROWID(目标表) */

使用范例:

1
2
3
select /*+ rowid(emp) */ empno,ename,sal,job
  from emp
 where rowid='AAAR3xAAEAAAACXAAA';

Oracle 11gR2中即使使用了ROWID Hint,Oracle还是会将读到的块缓存在Buffer Cache中。

三、与索引访问相关的Hint

1、INDEX

INDEX是针对单个目标表的Hint,它的含义是让优化器对目标表的的目标索引执行索引扫描操作。

INDEX Hint中的目标索引几乎可以是Oracle数据库中所有类型的索引(包括B树索引、位图索引、函数索引等)。

INDEX Hint的模式有四种:

格式1 /*+ INDEX(目标表 目标索引) */

格式2 /*+ INDEX(目标表 目标索引1 目标索引2 …… 目标索引n) */

格式3 /*+ INDEX(目标表 (目标索引1的索引列名) (目标索引2的索引列名) …… (目标索引n的索引列名)) */

格式4 /*+ INDEX(目标表) */

格式1表示仅指定了目标表上的一个目标索引,此时优化器只会考虑对这个目标索引执行索引扫描操作,而不会去考虑全表扫描或者对该目标表上的其他索引执行索引扫描操作。

格式2表示指定了目标表上的n个目标索引,此时优化器只会考虑对这n个目标索引执行索引扫描操作,而不会去考虑全表扫描或者对该目标表上的其他索引执行索引扫描操作。注意,优化器在考虑这n个目标索引时,可能是分别计算出单独扫描各个目标索引的成本后,再选择其中成本值最低的索引;也可能是先分别扫描目标索引中的两个或多个索引,然后再对扫描结果执行合并操作。当然,后面这种可能性的前提条件是优化器计算出来这样做的成本值是最低的。

格式三也是表是指定了目标表上的n个目标索引,只不过此时是用指定目标索引的索引列名来代替对应的目标索引名。如果目标索引是复合索引,则在用于指定该索引列名的括号内也可以指定该目标索引的多个索引列,各个索引列之间用空格分隔就可以了。

格式的表示指定了目标表上所有已存在的索引,此时优化器只会考虑对该目标表上所有已存在的索引执行索引扫描操作,而不会去考虑全表扫描操作。注意,这里优化器在考虑该目标表上所有已存在的索引时,可能是分别计算出单独扫描这些索引的成本后再选择其中成本值最低的索引;也可能是先分别扫描这些索引中的两个或多个索引,然后再对扫描结果执行合并操作。当然,后面这种可能性的前提条件是优化器计算出来这样做的成本值是最低的。

使用范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
select /*+ index(emp pk_emp) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
  
select /*+ index(emp pk_emp idx_emp_mgr idx_emp_dept) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
  
select /*+ index(emp (empno) (mgr) (deptno)) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
  
select /*+ index */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;

2、NO_INDEX

NO_INDEX是针对单个目标表的Hint,它是INDEX的反义Hint,其含义是让优化器不对目标表上的目标索引执行扫描操作。

INDEX Hint中的目标索引也几乎可以是Oracle数据库中所有类型的索引(包括B树索引、位图索引、函数索引等)。

格式有如下三种:

格式1 /*+ NO_INDEX(目标表 目标索引) */

格式2 /*+ NO_INDEX(目标表 目标索引1 目标索引2 …… 目标索引n) */

格式3 /*+ NO_INDEX(目标表) */

格式1表示仅指定了目标表上的一个目标索引,此时优化器只是不会考虑对这个目标索引执行索引扫描操作,但还是会考虑全表扫描或者对该目标表上的其他索引执行索引扫描操作。

格式2表示指定了目标表上的n个目标索引,此时优化器只是不会考虑对这n个目标索引执行索引扫描操作,但还是会考虑全表扫描或者对该目标表上的其他索引执行索引扫描操作。

格式3表示指定了目标表上的所有已存在的索引,即此时优化器不会考虑对该目标表上所有已存在的索引执行索引扫描操作,这相当于对目标表指定了全表扫描。

使用范例:

1
2
3
4
5
6
7
8
9
10
11
select /*+ no_index(emp pk_emp) */ empno,ename,sal,job
  from emp
where empno=7369 and mgr=7902 and deptno=20;
  
select /*+ no_index(emp pk_emp idx_emp_mgr idx_emp_dept) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
  
select /*+ no_index */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;

3、INDEX_DESC

INDEX_DESC是针对单个目标表的Hint,它的含义是让优化器对目标表上的目标索引执行索引降序扫描操作。如果目标索引是升序的,则INDEX_DESC Hint会使Oracle以降序的方式扫描该索引;如果目标索引是降序的,则INDEX_DESC Hint会使Oracle以升序的方式扫描该索引。

格式有三种:

格式1 /*+ INDEX_DESC(目标表 目标索引) */

格式2 /*+ INDEX_DESC(目标表 目标索引1 目标索引2 …… 目标索引n) */

格式3 /*+ INDEX_DESC(目标表) */

上述3种格式的含义和INDEX中对应格式的含义相同。

使用范例:

1
2
3
4
5
6
7
8
9
10
11
select /*+ index_desc(emp pk_emp) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
  
select /*+ index_desc(emp pk_emp idx_emp_mgr idx_emp_dept) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
  
select /*+ index_desc */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
scott@TEST>select /*+ index_desc(emp,pk_emp) */ empno from emp;
 
     EMPNO
----------
      7934
      7902
      7900
      7876
      7844
      7839
      7788
      7782
      7698
      7654
      7566
      7521
      7499
      7369
 
14 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1838043032
 
-------------------------------------------------------------------------------------
| Id  | Operation      
   | 
Name   Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT 
   |     |    10 |  40 |   1   (0)| 00:00:01 |
|   1 |  INDEX FULL SCAN DESCENDING| PK_EMP |   
 10 |  40 |   1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
......

4、INDEX_COMBINE

INDEX_COMBINE是针对单个目标表的Hint,它的含义是让优化器对目标表上的多个目标索引执行位图布尔运算。Oracle数据库里有一个映射函数(Mapping Function),它可以实例B*Tree索引中的ROWID和对应位图索引中的位图之间的互相转换,所以INDEX_COMBINE Hint并不局限于位图索引,它的作用对象也可以是B*Tree索引。

格式有如下两种

格式1 /*+ INDEX_COMBINE(目标表 目标索引1 目标索引2 …… 目标索引n) */

格式2 /*+ INDEX_COMBINE(目标表) */

格式1表示指定了目标表上的n个目标索引,此时优化器会考虑对这n个目标索引中的两个或多个执行位图布尔运算。

格式2表示指定了目标表上所有已存在的索引,此时优化器会考虑对该表上已存在的所有索引中的两个或多个执行位图布尔运算。

使用范例:

1
2
3
4
5
6
7
8
9
10
11
select /*+ index_combine(emp pk_emp idx_emp_mgr) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 ;
  
select /*+ index_combine(emp pk_emp idx_emp_mgr idx_emp_deptno) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;
  
select /*+ index_combine(emp) */ empno,ename,sal,job
  from emp
 where empno=7369 and mgr=7902 and deptno=20;

下面看一个实例,在表EMP上创建两个索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
scott@TEST>create index idx_emp_mgr on emp(mgr);
 
Index created.
 
scott@TEST>create index idx_emp_dept on emp(deptno);
 
Index created.
 
scott@TEST>select /*+ index_combine(emp pk_emp idx_emp_mgr idx_emp_deptno) */ empno,ename,sal,job
  2   from emp
  3   where empno=7369 and mgr=7902 and deptno=20;
 
     EMPNO ENAME             
 SAL JOB
---------- ------------------------------ ---------- ---------------------------
      7369 SMITH            
 800 CLERK
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1816402415
 
-------------------------------------------------------------------------------------------------
| Id  | Operation          
 | 
Name  Rows | Bytes | Cost (%CPU)| Time    |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     
 |     |     1 |    29 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID 
 | EMP        |     1 |    29 |     2   (0)| 00:00:01 |
|   2 |   BITMAP CONVERSION TO ROWIDS
 |     |   |   |        |     |
|   3 |    BITMAP AND         
 |    
|   |   |        |     |
|   4 |     BITMAP CONVERSION FROM ROWIDS|     
|   |   |        |     |
|*  5 |      INDEX RANGE SCAN       
 | PK_EMP |   |   |     0   (0)| 00:00:01 |
|   6 |     BITMAP CONVERSION FROM ROWIDS|     
|   |   |        |     |
|*  7 |      INDEX RANGE SCAN       
 | IDX_EMP_MGR    |   |   |     1   (0)| 00:00:01 |
|   8 |     BITMAP CONVERSION FROM ROWIDS|     
|   |   |        |     |
|*  9 |      INDEX RANGE SCAN       
 | IDX_EMP_DEPT |    |   |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
......

从上面的执行计划中可以看到关键字“BITMAP CONVERSION FROM ROWIDS”、“BITMAP AND”和“BITMAP CONVERSION TO ROWIDS”,这说明Oracle先分别对上述三个单键值的B*Tree索引IDX_EMP_MGR、IDX_EMP_DEPT和PK_EMP用映射函数将其中的ROWID转换成了位图,然后对转换后的位图执行了BITMAP AND(位图按位与)布尔运算,最后将布尔运算的结果再次用映射函数转换成了ROWID并回表得到最终的执行结果。能走出这样的执行计划显然是因为INDEX_COMBINE
Hint生效了。

用映射函数将ROWID转换成了位图,然后再执行布尔运算,最后将布尔运算的结果再次用映射函数转换成了ROWID并回表得到最终的执行结果,这个过程在实际生产环境中的执行效率可能是有问题的,可以使用隐含参数_B_TREE_BITMAP_PLANS禁掉该过程中的ROWID到位图的转换:

alter session set "_b_tree_bitmap_plans"=false;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
scott@TEST>alter session set "_b_tree_bitmap_plans"=false;
 
Session altered.
 
scott@TEST>select /*+ index_combine(emp pk_emp idx_emp_mgr idx_emp_deptno) */ empno,ename,sal,job
  2    from emp
  3   where empno=7369 and mgr=7902 and deptno=20;
 
     EMPNO ENAME             
 SAL JOB
---------- ------------------------------ ---------- ---------------------------
      7369 SMITH            
 800 CLERK
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 2949544139
 
--------------------------------------------------------------------------------------
| Id  | Operation      
    | 
Name   Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT 
    |        |    1 |    29 |      1   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP    |   
   1 |    29 |      1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN
    | PK_EMP |       1 |       |    0   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
......

从上面的执行计划中可以看出没有出现BITMAP相关的关键字,即INDEX_COMBINE Hint被Oracle忽略了。

5、INDEX_FFS

INDEX_FFS是针对单个目标表的Hint,它的含义是让优化器对目标表上的目标索引执行索引快速全扫描操作。注意,索引快速全扫描能成立的前提条件是SELECT语句中所有的查询列都存在于目标索引中,即通过扫描目标索引就可以得到所有的查询列而不用回表。

格式有如下三种:

格式1 /*+ INDEX_FFS(目标表 目标索引) */

格式2 /*+ INDEX_FFS(目标表 目标索引1 目标索引2 …… 目标索引n) */

格式3 /*+ INDEX_FFS(目标表) */

上述3种格式的含义和INDEX中对应格式的含义相同。

使用范例:

1
2
3
4
5
6
7
8
9
10
11
select /*+ index_ffs(emp pk_emp) */ empno
  from emp;
 
select /*+ index_ffs(emp idx_emp_1 idx_emp_2) */ empno
  from emp
 where mgr=7902 and deptno=20;
--create index idx_emp_1 on emp(mgr,deptno,1);
--create index idx_emp_2 on emp(mgr,deptno,2);
 
select /*+ index_ffs(emp) */ empno
  from emp;

看下面的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
scott@TEST>select empno from emp;
 
14 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 179099197
 
---------------------------------------------------------------------------
| Id  | Operation  
 | 
Name   Rows  | Bytes | Cost (%CPU)| Time 
  |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT |  
  |    10 |    40 | 1   (0)| 00:00:01 |
|   1 |  INDEX FULL SCAN | PK_EMP |    10 |    40 |   
1   (0)| 00:00:01 |
---------------------------------------------------------------------------
......
scott@TEST>select /*+ index_ffs(emp) */empno from emp;
 
14 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 366039554
 
-------------------------------------------------------------------------------
| Id  | Operation  
     | 
Name   Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |  
      |    10 |    40 |     2  (0)| 00:00:01 |
|   1 |  INDEX FAST FULL SCAN| PK_EMP |    10 |    40 |     2
(0)| 00:00:01 |
-------------------------------------------------------------------------------
......

6、INDEX_JOIN

INDEX_JOIN是针对单个目标表的Hint,它的含义是让优化器对目标表上的多个目标索引执行INDEX JOIN操作。INDEX JOIN能成立的前提条件是SELECT语句中所有的查询列都存在于目标表上的多个目标索引中,即通过扫描这些索引就可以得到所有的查询列而不用回表。

格式如下:

格式1 /*+ INDEX_JOIN(目标表 目标索引1 目标索引2 …… 目标索引n) */

格式2 /*+ INDEX_JOIN */

上述两种格式的含义与INDEX_COMBINE Hint中对应格式的含义相同。

使用范例:

1
2
3
4
5
6
7
select /*+ index_join(emp pk_emp idx_emp_mgr) */ empno,mgr
  from emp
 where empno>7369 and mgr<7902;
  
select /*+ index_join(emp) */ empno,mgr
  from emp
 where empno>7369 and mgr<7902;

来看下面的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
scott@TEST>select empno,mgr
  2    from emp
  3   where empno>7369 and mgr<7902;
 
12 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 2059184959
 
-------------------------------------------------------------------------------------------
| Id  | Operation      
    | 
Name   Rows  | Bytes | Cost (%CPU)| Time   |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT 
    |         |    10 |    80 | 2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP
  |    10 |    80 | 2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN 
    | IDX_EMP_MGR |    11 |    |   1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
......
scott@TEST>select /*+ index_join(emp) */ empno,mgr
  2    from emp
  3   where empno>7369 and mgr<7902;
 
12 rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3030719951
 
---------------------------------------------------------------------------------------
| Id  | Operation  
   | 
Name  
      Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      
      |    10 |    80 |     3  (34)| 00:00:01 |
|*  1 |  VIEW     
   index$_join$_001 |    10 |    80 |     3  (34)| 00:00:01 |
|*  2 |   HASH JOIN 
   |    
      |       |       |       |       |
|*  3 |    INDEX RANGE SCAN| IDX_EMP_MGR      |    10 |    80 |     2  (50)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN| PK_EMP  
      |    10 |    80 |     2  (50)| 00:00:01 |
---------------------------------------------------------------------------------------
......

7、AND_EQUAL

AND_EQUAL是针对单个目标表的Hint,它的含义是让优化器对目标表上的多个目标索引执行INDEX MERGE操作。INDEX MERGE能成立的前提条件是目标SQL的where条件里出现了多个针对不同单列的等值条件,并且这些列上都有单键值的索引。另外,在Oracle数据库里,能够做INDEX MERGE的索引数量的最大值是5。

格式如下:

/*+ AND_EQUAL(目标表 目标索引1 目标索引2 …… 目标索引n)*/

使用范例:

1
2
3
select /*+ and_equal(emp idx_emp_mgr idx_emp_dept) */ empno,mgr
  from emp
 where deptno=20 and mgr=7902;

看下面的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
scott@TEST>select empno,mgr
  2    from emp
  3   where deptno=20 and mgr=7902;
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 2059184959
 
-------------------------------------------------------------------------------------------
| Id  | Operation      
    | 
Name   Rows  | Bytes | Cost (%CPU)| Time   |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT 
    |         |   1 |    11 |   2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP
  |   1 |    11 |   2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN 
    | IDX_EMP_MGR |   2 |      |   1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
......
scott@TEST>select /*+ and_equal(emp idx_emp_mgr idx_emp_dept) */ empno,mgr
  2    from emp
  3   where deptno=20 and mgr=7902;
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3295440569
 
--------------------------------------------------------------------------------------------
| Id  | Operation      
    | 
Name    Rows  | Bytes | Cost (%CPU)| Time  
   |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT 
    |          |  1 |  11 |    3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP
   |  1 |  11 |    3   (0)| 00:00:01 |
|   2 |   AND-EQUAL     
    |          |    |    |     |      |
|*  3 |    INDEX RANGE SCAN   
    | IDX_EMP_MGR  |  2 |     |  1   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN   
    | IDX_EMP_DEPT |   5 |     |  1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
......

四、与表连接顺序相关的Hint

1、ORDERED

ORDERED是针对多个目标表的Hint,它的含义是让优化器对多个目标表执行表连接操作时,执照它们在目标SQL的where条件中出现的顺序从左到右依次进行连接。

格式如下:

/*+ ORDERED */

使用范例:

1
2
3
4
5
6
select /*+ ordered */ e.ename,j.job,e.sal,d.deptno
  from emp e,jobs j,dept d 
 where e.empno=j.empno
   and e.deptno=d.deptno
   and d.loc='CHICAGO'
 order by e.ename;

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
scott@TEST>select e.ename,j.job,e.sal,d.deptno
  2    from emp e,jobs j,dept d 
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6   order by e.ename;
 
rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 4113290228
 
-----------------------------------------------------------------------------------------------
| Id  | Operation                      | Name         Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |              |     5 |   235 |     9  (23)| 00:00:01 |
|   1 |  SORT ORDER BY                 |              |     5 |   235 |     9  (23)| 00:00:01 |
|*  2 |   HASH JOIN                    |              |     5 |   235 |     8  (13)| 00:00:01 |
|   3 |    NESTED LOOPS                |              |       |       |            |          |
|   4 |     NESTED LOOPS               |              |     5 |   140 |     4   (0)| 00:00:01 |
|*  5 |      TABLE ACCESS FULL         | DEPT         |     1 |    11 |     3   (0)| 00:00:01 |
|*  6 |      INDEX RANGE SCAN          | IDX_EMP_DEPT |     5 |       |     0   (0)| 00:00:01 |
|   7 |     TABLE ACCESS BY INDEX ROWID| EMP          |     5 |    85 |     1   (0)| 00:00:01 |
|   8 |    TABLE ACCESS FULL           | JOBS         |    14 |   266 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
......
scott@TEST>select /*+ ordered */ e.ename,j.job,e.sal,d.deptno
  2    from emp e,jobs j,dept d 
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6   order by e.ename;
 
rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3031293267
 
-----------------------------------------------------------------------------------------
| Id  | Operation                      | Name   Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |        |     5 |   235 |    11  (28)| 00:00:01 |
|   1 |  SORT ORDER BY                 |        |     5 |   235 |    11  (28)| 00:00:01 |
|*  2 |   HASH JOIN                    |        |     5 |   235 |    10  (20)| 00:00:01 |
|   3 |    MERGE JOIN                  |        |    14 |   504 |     6  (17)| 00:00:01 |
|   4 |     TABLE ACCESS BY INDEX ROWID| EMP    |    14 |   238 |     2   (0)| 00:00:01 |
|   5 |      INDEX FULL SCAN           | PK_EMP |    14 |       |     1   (0)| 00:00:01 |
|*  6 |     SORT JOIN                  |        |    14 |   266 |     4  (25)| 00:00:01 |
|   7 |      TABLE ACCESS FULL         | JOBS   |    14 |   266 |     3   (0)| 00:00:01 |
|*  8 |    TABLE ACCESS FULL           | DEPT   |     1 |    11 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
......
scott@TEST>select /*+ ordered */ e.ename,j.job,e.sal,d.deptno
  2    from emp e,dept d,jobs j
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6   order by e.ename;
 
rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 1175157407
 
-----------------------------------------------------------------------------------------------
| Id  | Operation                      | Name         Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |              |     5 |   235 |    11  (28)| 00:00:01 |
|   1 |  SORT ORDER BY                 |              |     5 |   235 |    11  (28)| 00:00:01 |
|*  2 |   HASH JOIN                    |              |     5 |   235 |    10  (20)| 00:00:01 |
|   3 |    MERGE JOIN                  |              |     5 |   140 |     6  (17)| 00:00:01 |
|   4 |     TABLE ACCESS BY INDEX ROWID| EMP          |    14 |   238 |     2   (0)| 00:00:01 |
|   5 |      INDEX FULL SCAN           | IDX_EMP_DEPT |    14 |       |     1   (0)| 00:00:01 |
|*  6 |     SORT JOIN                  |              |     1 |    11 |     4  (25)| 00:00:01 |
|*  7 |      TABLE ACCESS FULL         | DEPT         |     1 |    11 |     3   (0)| 00:00:01 |
|   8 |    TABLE ACCESS FULL           | JOBS         |    14 |   266 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
......

从上面的执行计划可以看出不使用ordered Hint时表扫描的顺序是DEPT->EMP->JOBS,但是使用ordered Hint后,表扫描的顺序变为了EMP->JOBS->DEPT与目标SQL中的顺序一致了,在修改了目标SQL文本之后表的扫描顺序也相应地变为了EMP->DEPT->JOBS。

2、LEADING

LEADING是针对多个目标表的Hint,它的含义是让优化器将我们指定的多个表的连接结果作为目标SQL表连接过程中的驱动结果集,并且将LEADING Hint中从左至右出现的第一个目标表作为整个表连接过程中的首个驱动表。

LEADING比ORDERED要温和一些,因为它只是指定了首个驱动表和驱动结果集,没有像ORDERED那样完全指定了表连接的顺序,也就是说LEADING给了优化器更大的调整余地。

当LEADING Hint中指定的表并不能作为目标SQL的连接过程中的驱动表或者驱动结果集时,Oracle会忽略该Hint。

格式如下:

/*+ LEADING(目标表1 目标表2 …… 目标表n) */

使用范例:

1
2
3
4
5
6
7
select /*+ leading(t e) */ e.ename,j.job,e.sal,d.deptno
  from emp e,jobs j,dept d,emp_temp t 
 where e.empno=j.empno
   and e.deptno=d.deptno
   and d.loc='CHICAGO'
   and e.ename=t.ename
 order by e.ename;

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
--不使用Hint
scott@TEST>select e.ename,j.job,e.sal,d.deptno
  2    from emp e,jobs j,dept d,emp_temp t 
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6     and e.ename=t.ename
  7   order by e.ename;
 
rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 558051962
 
------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name         Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |              |     5 |   270 |    12  (17)| 00:00:01 |
|   1 |  SORT ORDER BY                  |              |     5 |   270 |    12  (17)| 00:00:01 |
|*  2 |   HASH JOIN                     |              |     5 |   270 |    11  (10)| 00:00:01 |
|*  3 |    HASH JOIN                    |              |     5 |   235 |     8  (13)| 00:00:01 |
|   4 |     NESTED LOOPS                |              |       |       |            |          |
|   5 |      NESTED LOOPS               |              |     5 |   140 |     4   (0)| 00:00:01 |
|*  6 |       TABLE ACCESS FULL         | DEPT         |     1 |    11 |     3   (0)| 00:00:01 |
|*  7 |       INDEX RANGE SCAN          | IDX_EMP_DEPT |     5 |       |     0   (0)| 00:00:01 |
|   8 |      TABLE ACCESS BY INDEX ROWID| EMP          |     5 |    85 |     1   (0)| 00:00:01 |
|   9 |     TABLE ACCESS FULL           | JOBS         |    14 |   266 |     3   (0)| 00:00:01 |
|  10 |    TABLE ACCESS FULL            | EMP_TEMP     |    14 |    98 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------
......
--使用LEADING Hint
scott@TEST>select /*+ leading(t e) */ e.ename,j.job,e.sal,d.deptno
  2    from emp e,jobs j,dept d,emp_temp t 
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6     and e.ename=t.ename
  7   order by e.ename;
 
rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 937897748
 
----------------------------------------------------------------------------------
| Id  | Operation             | Name     Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |          |     5 |   270 |    15  (20)| 00:00:01 |
|   1 |  SORT ORDER BY        |          |     5 |   270 |    15  (20)| 00:00:01 |
|*  2 |   HASH JOIN           |          |     5 |   270 |    14  (15)| 00:00:01 |
|*  3 |    HASH JOIN          |          |     5 |   175 |    10  (10)| 00:00:01 |
|*  4 |     HASH JOIN         |          |    14 |   336 |     7  (15)| 00:00:01 |
|   5 |      TABLE ACCESS FULL| EMP_TEMP |    14 |    98 |     3   (0)| 00:00:01 |
|   6 |      TABLE ACCESS FULL| EMP      |    14 |   238 |     3   (0)| 00:00:01 |
|*  7 |     TABLE ACCESS FULL | DEPT     |     1 |    11 |     3   (0)| 00:00:01 |
|   8 |    TABLE ACCESS FULL  | JOBS     |    14 |   266 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------
......
--使用Ordered Hint
scott@TEST>select /*+ ordered */ e.ename,j.job,e.sal,d.deptno
  2    from emp e,jobs j,dept d,emp_temp t 
  3   where e.empno=j.empno
  4     and e.deptno=d.deptno
  5     and d.loc='CHICAGO'
  6     and e.ename=t.ename
  7   order by e.ename;
 
rows selected.
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 2459794491
 
--------------------------------------------------------------------------------------------
| Id  | Operation                       | Name     Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |          |     5 |   270 |    14  (22)| 00:00:01 |
|   1 |  SORT ORDER BY                  |          |     5 |   270 |    14  (22)| 00:00:01 |
|*  2 |   HASH JOIN                     |          |     5 |   270 |    13  (16)| 00:00:01 |
|*  3 |    HASH JOIN                    |          |     5 |   235 |    10  (20)| 00:00:01 |
|   4 |     MERGE JOIN                  |          |    14 |   504 |     6  (17)| 00:00:01 |
|   5 |      TABLE ACCESS BY INDEX ROWID| EMP      |    14 |   238 |     2   (0)| 00:00:01 |
|   6 |       INDEX FULL SCAN           | PK_EMP   |    14 |       |     1   (0)| 00:00:01 |
|*  7 |      SORT JOIN                  |          |    14 |   266 |     4  (25)| 00:00:01 |
|   8 |       TABLE ACCESS FULL         | JOBS     |    14 |   266 |     3   (0)| 00:00:01 |
|*  9 |     TABLE ACCESS FULL           | DEPT     |     1 |    11 |     3   (0)| 00:00:01 |
|  10 |    TABLE ACCESS FULL            | EMP_TEMP |    14 |    98 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
......

从上面的执行计划可以看出不使用Hint时表扫描顺序是DEPT->EMP->JOBS->EMP_TEMP;使用LEADING Hint时表扫描顺序是EMP_TEMP->EMP->DEPT->JOBS,EMP_TEMP做首个驱动表和表EMP的连接结果做为驱动结果集,与Hint要求一致。;使用Ordered Hint时表扫描顺序是EMP->JOBS->DEPT->EMP_TEMP,与SQL中顺序一致。

原文地址:https://blog.csdn.net/tmchongye/article/details/64389420

Oracle中常见的Hint(一)的更多相关文章

  1. ORACLE中常见的几种锁

    ORACLE中常见的几种锁: 0:none 1:null 空 2:Row-S 行共享(RS):共享表锁,sub share 3:Row-X 行独占(RX):用于行的修改,sub exclusive 4 ...

  2. Oracle中常见的33个等待事件小结

    在Oracle 10g中的等待事件有872个,11g中等待事件1116个. 我们可以通过v$event_name 视图来查看等待事件的相关信息     一. 等待事件的相关知识 1.1 等待事件主要可 ...

  3. Oracle性能优化之oracle中常见的执行计划及其简单解释

    一.访问表执行计划 1.table access full:全表扫描.它会访问表中的每一条记录(读取高水位线以内的每一个数据块). 2.table access by user rowid:输入源ro ...

  4. ORACLE中常见SET指令

    1         SET TIMING ON 说明:显示SQL语句的运行时间.默认值为OFF. 在SQLPLUS中使用,时间精确到0.01秒.也就是10毫秒. 在PL/SQL DEVELOPER 中 ...

  5. Oracle中常见表与各类结构的查询

    ----------------------------------------------------------------------用户--查询:当前用户的缺省表空间select userna ...

  6. oracle中常见的对表、表空间和视图的操作

    创建表:create table t1(key1 type default 0,key2 type not null) 删除表:drop table t1; 删除表数据:truncate table ...

  7. oracle中常见的查询操作

    普通查询:select * from t; 去除重复值:select distinct f1,f2 from t; between用法:select * from t where f1 not/bet ...

  8. SQL优化过程中常见Oracle HINT

    在SQL语句优化过程中,我们经常会用到hint,现总结一下在SQL优化过程中常见Oracle HINT的用法: 1. /*+ALL_ROWS*/ 表明对语句块选择基于开销的优化方法,并获得最佳吞吐量, ...

  9. 2、Java应用中常见的JDBC连接字符串(SQLite、MySQL、Oracle、Sybase、SQLServer、DB2)

    2.Java应用中常见的JDBC连接字符串 Java应用中连接数据库是不可或缺的,于是便整理一些可能用到的JDBC的jar包及其相匹配的URL,以备日后查阅. 1)SQLite Class.forNa ...

随机推荐

  1. 为什么要使用MQ和到底什么时候要使用MQ

      一.缘起 一切脱离业务的架构设计与新技术引入都是耍流氓. 引入一个技术之前,首先应该解答的问题是,这个技术解决什么问题. 就像微服务分层架构之前,应该首先回答,为什么要引入微服务,微服务究竟解决什 ...

  2. 蛋糕仙人的javascript笔记

    蛋糕仙人的javascript笔记:https://www.w3cschool.cn/kesyi/kesyi-nqej24rv.html

  3. BZOJ 4765(分块+树状数组)

    题面 传送门 "奋战三星期,造台计算机".小G响应号召,花了三小时造了台普通计算姬.普通计算姬比普通计算机要厉害一些 .普通计算机能计算数列区间和,而普通计算姬能计算树中子树和.更 ...

  4. 北京邀请赛 B. Beautiful Garden

    题意:给你坐标和n个点,求最少移动的点使得n个点成等差数列 思路:既然要成等差数列,那么最起码有两个点是不动的,然后枚举这两个点中间的点的个数,近期水的要死,看了队友的代码做的 #include &l ...

  5. Mybatis一级缓存和二级缓存 Redis缓存

    一级缓存 Mybatis的一级缓存存放在SqlSession的生命周期,在同一个SqlSession中查询时,Mybatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对 ...

  6. LLppdd likes strings

    LLppdd's likes strings! Time Limit: 1 s Memory Limit: 256 MB 题目背景 LLppdd 由于实在是太弱了,在 \(ION 2018\) 模拟十 ...

  7. JavaScript 事件——“模拟事件”的注意要点

    DOM中的事件模拟 三个步骤: 首先通过document.createEvent()方法创建event对象,接收一个参数,即表示要创建的事件类型的字符串: UIEvents(DOM3中的UIEvent ...

  8. eclipse里部署struts2

    Struts2是一个比较出色的基于MVC设计模式的框架,是由Struts1和WebWork发展而来的,性能也比较稳定,现在是Apache软件基金会的一个项目,下面就来配置Struts2进行初始化的开发 ...

  9. sparksql笔记

    1.sparksql是Spark用来处理结构化数据的一个模块,它提供了两个抽象DataFrame和DataSet并且作为分布式SQL查询引擎的作用. Hive SQL转换成MapReduce然后提交到 ...

  10. UNP学习第六章(二)

    一.描述符就绪条件 对于引起select返回套接字“就绪”的条件我们必须讨论得更明确: (1)满足一下塞个条件中的仍和一个时,一个套接字准备好读. a)该套接字接收缓冲区中的数据字节数不大于等于套接字 ...