数据仓库中加工数据时,经常会使用提示(HINT)指示优化器改变执行路径,以便快速的得到数据。现总结一下提示的使用。内容主要来源于Oracle docs 和网络。

优化器提示

1.优化器提示概述

1.1 提示的类型

提示可以是以下几种一般的类型:
1.单表:提示指定在一个表或这视图上,例如INDEX 和USE_NL;
2.多表:与单表提示类似,只是提示能够指定在一个或多个表和视图上。例如LEADING是一个多表提示。注意USE_NL(table1 table2)不是多表提示,只是USE_NL(table1)和USE_NL(table2)的简写;
3.查询块:作用在单个查询块。例如STAR_TRANSFORMATION和UNNEST;
4.语句:语句提示应用到整个SQL语句。例如ALL_ROWS。

1.2 提示的分类

(1) 指定优化方法和目标的提示

ALL_ROWS和FIRST_ROWS提示能够让你选择优化器的优化方法和目标。如果一个SQL语句中有一个提示指定了优化的方法和目标,那么优化器不会考虑是否存在统计数据、初始化参数OPTIMIZER_MODE的值和ALTER SESSION语句修改了OPTIMIZER_MODE的值,都会使用指定的路径。
如果你在SQL语句中指定了ALL_ROWS或者FIRST_ROWS(n),并且访问表在数据字典中没有统计数据,那么优化器会使用默认统计值(如表中的存储分布)估算缺失的统计数据,并以此生成执行计划。估计出的统计数据没有用DBMS_STATS包生成的准,所以应该使用包收集统计数据。
如果你通过使用提示指定了访问途径或联接操作,并且指定了ALL_ROWS或者FIRST_ROWS(n)提示,那么优化器将优先考虑由提示指定的访问路径和连接操作。

数据库实例中有一个默认的目标设置,他指定了当执行一个查询时,要达到的目标是(1)快速响应,尽快的返回一些行-FIRST_ROWS;还是(2)牺牲前期处理时间以实现整体成本最小化-ALL_ROWS;
查看默认优化目标:

SQL> show parameter optimizer_mode
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
optimizer_mode                       string      ALL_ROWS

默认查询示例:

SELECT * FROM EMPLOYEES NATURAL JOIN DEPARTMENTS;-----------------------------------------------------------------------------
| Id  | Operation            | Name        | Rows | Bytes | Cost | Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |   13 |  1209 |    7 | 00:00:01 |
| * 1 |   HASH JOIN          |             |   13 |  1209 |    7 | 00:00:01 |
| * 2 |    TABLE ACCESS FULL | DEPARTMENTS |   13 |   273 |    3 | 00:00:01 |
|   3 |    TABLE ACCESS FULL | EMPLOYEES   |  115 |  8280 |    3 | 00:00:01 |
-----------------------------------------------------------------------------

默认使用了全表扫描。如果向要尽快的看到数据,可以使用FIRST_ROWS提示,改变执行计划,以尽快返回查询结果。
FIRST_ROWS查询示例:

SELECT/*+ FIRST_ROWS */ * FROM EMPLOYEES NATURAL JOIN DEPARTMENTS;--------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name              | Rows | Bytes | Cost | Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                   |   13 |  1209 |   11 | 00:00:01 |
|   1 |   NESTED LOOPS                      |                   |      |       |      |          |
|   2 |    NESTED LOOPS                     |                   |   13 |  1209 |   11 | 00:00:01 |
| * 3 |     TABLE ACCESS FULL               | DEPARTMENTS       |   13 |   273 |    3 | 00:00:01 |
|   4 |     BITMAP CONVERSION TO ROWIDS     |                   |      |       |      |          |
|   5 |      BITMAP AND                     |                   |      |       |      |          |
|   6 |       BITMAP CONVERSION FROM ROWIDS |                   |      |       |      |          |
| * 7 |        INDEX RANGE SCAN             | EMP_MANAGER_IX    |   10 |       |    0 | 00:00:01 |
|   8 |       BITMAP CONVERSION FROM ROWIDS |                   |      |       |      |          |
| * 9 |        INDEX RANGE SCAN             | EMP_DEPARTMENT_IX |   10 |       |    0 | 00:00:01 |
|  10 |    TABLE ACCESS BY INDEX ROWID      | EMPLOYEES         |    1 |    72 |   11 | 00:00:01 |
--------------------------------------------------------------------------------------------------

(2) 启用优化器特性的提示

OPTIMIZER_FEATURES_ENABLE提示通常用来启用基于数据库版本号的优化器特性。使用这个提示的主要原因就是在数据库升级之后,某些查询的性能显著下降了,在找到新的解决方案之前,使用这个提示临时解决性能问题。
提示使用示例:

SELECT/*+ OPTIMIZER_FEATURES_ENABLE('10.2')*/ * FROM employees JOIN departments USING (department_id);

(3) 指定访问路径的提示

以下提示指示优化器按照指定的路径访问表:
FULL
CLUSTER
HASH
INDEX 和 NO_INDEX
INDEX_ASC 和 INDEX_DESC
INDEX_COMBINE 和 INDEX_JOIN
INDEX_JOIN
INDEX_FFS 和 NO_INDEX_FFS
INDEX_SS 和 NO_INDEX_SS
INDEX_SS_ASC 和 INDEX_SS_DESC

只有SQL语句的语法结构中的索引或簇有效时,指定的提示才会导致优化器选择指定的访问路径。如果指定的访问路径不可用,优化器会忽略它。如果语句为表使用别名,则在提示中使用别名而不是表名。如果模式名出现在语句中,则提示中的表名不应包含模式名。
常用的改变访问路径的提示-full:
不加提示:走了索引范围扫描

SELECT emp.employee_id, emp.first_name,emp.last_nameFROM employees empWHERE emp.department_id=20;--------------------------------------------------------------------------------------------
| Id  | Operation                     | Name              | Rows | Bytes | Cost | Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                   |    2 |    46 |    2 | 00:00:01 |
|   1 |   TABLE ACCESS BY INDEX ROWID | EMPLOYEES         |    2 |    46 |    2 | 00:00:01 |
| * 2 |    INDEX RANGE SCAN           | EMP_DEPARTMENT_IX |    2 |       |    1 | 00:00:01 |
--------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("EMP"."DEPARTMENT_ID"=20)

添加full提示:按照指定走全表扫描

SELECT/*+ full(emp) */ emp.employee_id, emp.first_name,emp.last_nameFROM employees empWHERE emp.department_id=20;--------------------------------------------------------------------------
| Id  | Operation           | Name      | Rows | Bytes | Cost | Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |           |    2 |    46 |    3 | 00:00:01 |
| * 1 |   TABLE ACCESS FULL | EMPLOYEES |    2 |    46 |    3 | 00:00:01 |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter("EMP"."DEPARTMENT_ID"=20)

常用的改变访问路径的提示-no_index:
添加no_index提示:指定不要使用表上指定的索引查询数据

SELECT/*+ no_index(emp EMP_DEPARTMENT_IX) */ emp.employee_id, emp.first_name,emp.last_nameFROM employees empWHERE emp.department_id=20;--------------------------------------------------------------------------
| Id  | Operation           | Name      | Rows | Bytes | Cost | Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |           |    2 |    46 |    3 | 00:00:01 |
| * 1 |   TABLE ACCESS FULL | EMPLOYEES |    2 |    46 |    3 | 00:00:01 |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter("EMP"."DEPARTMENT_ID"=20)

常用的改变访问路径的提示-index:
没添加index提示查询:

SELECT emp.employee_id, emp.first_name,emp.last_nameFROM employees emp;--------------------------------------------------------------------------------------
| Id  | Operation                | Name             | Rows | Bytes | Cost | Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |                  |  115 |  2185 |    3 | 00:00:01 |
|   1 |   VIEW                   | index$_join$_001 |  115 |  2185 |    3 | 00:00:01 |
| * 2 |    HASH JOIN             |                  |      |       |      |          |
|   3 |     INDEX FAST FULL SCAN | EMP_NAME_IX      |  115 |  2185 |    1 | 00:00:01 |
|   4 |     INDEX FAST FULL SCAN | EMP_EMP_ID_PK    |  115 |  2185 |    1 | 00:00:01 |
--------------------------------------------------------------------------------------Predicate Information (identified by operation id):
------------------------------------------
* 2 - access(ROWID=ROWID)

添加index提示查询:没添加之前两个索引扫描,指定之后就使用指定的那个

SELECT/*+ index(emp EMP_NAME_IX)*/ emp.employee_id, emp.first_name,emp.last_nameFROM employees emp;-------------------------------------------------------------------------------------
| Id | Operation                     | Name        | Rows | Bytes | Cost | Time     |
-------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT              |             |  115 |  2185 |   28 | 00:00:01 |
|  1 |   TABLE ACCESS BY INDEX ROWID | EMPLOYEES   |  115 |  2185 |   28 | 00:00:01 |
|  2 |    INDEX FULL SCAN            | EMP_NAME_IX |  115 |       |    1 | 00:00:01 |
-------------------------------------------------------------------------------------

添加index提示查询:添加了未列出列department_id的索引提示,并没有使用

SELECT/*+ index(emp EMP_DEPARTMENT_IX)*/ emp.employee_id, emp.first_name,emp.last_nameFROM employees emp;--------------------------------------------------------------------------------------
| Id  | Operation                | Name             | Rows | Bytes | Cost | Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |                  |  115 |  2185 |    3 | 00:00:01 |
|   1 |   VIEW                   | index$_join$_001 |  115 |  2185 |    3 | 00:00:01 |
| * 2 |    HASH JOIN             |                  |      |       |      |          |
|   3 |     INDEX FAST FULL SCAN | EMP_NAME_IX      |  115 |  2185 |    1 | 00:00:01 |
|   4 |     INDEX FAST FULL SCAN | EMP_EMP_ID_PK    |  115 |  2185 |    1 | 00:00:01 |
--------------------------------------------------------------------------------------Predicate Information (identified by operation id):
------------------------------------------
* 2 - access(ROWID=ROWID)

提示影响优化器,但优化器可能会选择忽略查询中指定的提示。

索引提示的其他例子: index_ffs 索引快速全扫描(SELECT语句中所有的查询列都存在于目标索引中,即通过扫描目标索引就可以得到所有的查询列而不用回表),index_ss 索引跳跃扫描(适用于多列组合索引)。使用index_ss索引提示,即使where子句没有使用索引的引导列,数据库还可以使用该索引。

(4) 指定联接顺序的提示

改变联接顺序的提示有两个:ordered和leading。
对于ordered:它指示优化器按照from子句后面表从左到右出现的顺序联接表;
对于leading:它指示优化器联接表的顺序,并且将leading从左至右出现的第一个表作为驱动表;
测试ordered:

SELECT first_name ,department_nameFROM employees  JOIN departments USING (department_id);---------------------------------------------------------------------------------------------
| Id  | Operation                      | Name              | Rows | Bytes | Cost | Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                   |  114 |  3078 |    6 | 00:00:01 |
|   1 |   MERGE JOIN                   |                   |  114 |  3078 |    6 | 00:00:01 |
|   2 |    TABLE ACCESS BY INDEX ROWID | DEPARTMENTS       |   27 |   432 |    2 | 00:00:01 |
|   3 |     INDEX FULL SCAN            | DEPT_ID_PK        |   27 |       |    1 | 00:00:01 |
| * 4 |    SORT JOIN                   |                   |  115 |  1265 |    4 | 00:00:01 |
|   5 |     VIEW                       | index$_join$_001  |  115 |  1265 |    3 | 00:00:01 |
| * 6 |      HASH JOIN                 |                   |      |       |      |          |
|   7 |       INDEX FAST FULL SCAN     | EMP_DEPARTMENT_IX |  115 |  1265 |    1 | 00:00:01 |
|   8 |       INDEX FAST FULL SCAN     | EMP_NAME_IX       |  115 |  1265 |    1 | 00:00:01 |
---------------------------------------------------------------------------------------------

未指定提示,此时的联接顺序为 DEPARTMENTS -》 EMPLOYEES;
此时指定ordered提示:

SELECT/*+ ordered */ first_name ,department_nameFROM employees  JOIN departments USING (department_id);----------------------------------------------------------------------------------------
| Id  | Operation                 | Name              | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |                   |  114 |  3078 |    6 | 00:00:01 |
| * 1 |   HASH JOIN               |                   |  114 |  3078 |    6 | 00:00:01 |
|   2 |    VIEW                   | index$_join$_001  |  115 |  1265 |    3 | 00:00:01 |
| * 3 |     HASH JOIN             |                   |      |       |      |          |
|   4 |      INDEX FAST FULL SCAN | EMP_DEPARTMENT_IX |  115 |  1265 |    1 | 00:00:01 |
|   5 |      INDEX FAST FULL SCAN | EMP_NAME_IX       |  115 |  1265 |    1 | 00:00:01 |
|   6 |    TABLE ACCESS FULL      | DEPARTMENTS       |   27 |   432 |    3 | 00:00:01 |
----------------------------------------------------------------------------------------

指定ordered提示后,此时的联接顺序为from子句中出现的顺序:EMPLOYEES-》 DEPARTMENTS ;

测试leading提示:

--先emp,后dept
SELECT/*+ leading(employees ,departments) */ first_name ,department_nameFROM employees  JOIN departments USING (department_id);----------------------------------------------------------------------------------------
| Id  | Operation                 | Name              | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |                   |  114 |  3078 |    6 | 00:00:01 |
| * 1 |   HASH JOIN               |                   |  114 |  3078 |    6 | 00:00:01 |
|   2 |    VIEW                   | index$_join$_001  |  115 |  1265 |    3 | 00:00:01 |
| * 3 |     HASH JOIN             |                   |      |       |      |          |
|   4 |      INDEX FAST FULL SCAN | EMP_DEPARTMENT_IX |  115 |  1265 |    1 | 00:00:01 |
|   5 |      INDEX FAST FULL SCAN | EMP_NAME_IX       |  115 |  1265 |    1 | 00:00:01 |
|   6 |    TABLE ACCESS FULL      | DEPARTMENTS       |   27 |   432 |    3 | 00:00:01 |
------------------------------------------------------------------------------------------先dept,后 emp
SELECT/*+ leading(departments ,employees) */ first_name ,department_nameFROM employees  JOIN departments USING (department_id);---------------------------------------------------------------------------------------------
| Id  | Operation                      | Name              | Rows | Bytes | Cost | Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                   |  114 |  3078 |    6 | 00:00:01 |
|   1 |   MERGE JOIN                   |                   |  114 |  3078 |    6 | 00:00:01 |
|   2 |    TABLE ACCESS BY INDEX ROWID | DEPARTMENTS       |   27 |   432 |    2 | 00:00:01 |
|   3 |     INDEX FULL SCAN            | DEPT_ID_PK        |   27 |       |    1 | 00:00:01 |
| * 4 |    SORT JOIN                   |                   |  115 |  1265 |    4 | 00:00:01 |
|   5 |     VIEW                       | index$_join$_001  |  115 |  1265 |    3 | 00:00:01 |
| * 6 |      HASH JOIN                 |                   |      |       |      |          |
|   7 |       INDEX FAST FULL SCAN     | EMP_DEPARTMENT_IX |  115 |  1265 |    1 | 00:00:01 |
|   8 |       INDEX FAST FULL SCAN     | EMP_NAME_IX       |  115 |  1265 |    1 | 00:00:01 |
---------------------------------------------------------------------------------------------

当ordered提示和leading提示同时出现时,数据库会优先使用leading提示。

(5) 指定联接方式的提示

以下提示指示优化器使用特定的联接方式联接表:

USE_NL 和 NO_USE_NL
USE_NL_WITH_INDEX
USE_MERGE 和 NO_USE_MERGE
USE_HASH 和 NO_USE_HASH

有三种可能的联接方式:嵌套循环联接、排序合并联接和哈希散列联接;

方法 提示 描述
嵌套循环 USE_NL/NO_USE_NL/USE_NL_WITH_INDEX 该联接在处理少量数据时很有效。优化器会选择一个驱动表,作为联接时的外层表。对于外行表中的每一行数据,会在内层表中搜索一遍
排序合并 USE_MERGE/NO_USE_MERGE 排序合并联接对于预先排好序的数据行和全表扫描来说是很理想的。排序合并联接适用于非等值联接条件。两张表都按照联接键来排序,然后进行合并。对于大数据集来说,它比嵌套循环联接的性能好
哈希散列 USE_HASH/NO_USE_HASH 哈希散列联接在处理大量数据行时很有效。散列联接只适用于等值条件联接

指定嵌套循环联接:适合小表联接大表,小表作为外层“驱动”表,小表的每一行都会在大表中搜索一遍

-------------------先是以employees为驱动表,emp在计划的最外层
SELECT/*+ use_nl(employees ,departments ) */ first_name ,department_nameFROM employees  JOIN departments USING (department_id);---------------------------------------------------------------------------------------------
| Id  | Operation                      | Name              | Rows | Bytes | Cost | Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                   |  114 |  3078 |   25 | 00:00:01 |
|   1 |   NESTED LOOPS                 |                   |      |       |      |          |
|   2 |    NESTED LOOPS                |                   |  114 |  3078 |   25 | 00:00:01 |
|   3 |     TABLE ACCESS FULL          | DEPARTMENTS       |   27 |   432 |    3 | 00:00:01 |
| * 4 |     INDEX RANGE SCAN           | EMP_DEPARTMENT_IX |   10 |       |    0 | 00:00:01 |
|   5 |    TABLE ACCESS BY INDEX ROWID | EMPLOYEES         |    4 |    44 |    2 | 00:00:01 |
---------------------------------------------------------------------------------------------
---------------------------------这次以departments为驱动表,计划没有改变,所以添加了leading 提示指出先联接employees
SELECT/*+ leading(employees,departments) use_nl(departments ,employees ) */ last_name ,department_nameFROM employees  JOIN departments USING (department_id);---------------------------------------------------------------------------------------------
| Id  | Operation                      | Name              | Rows | Bytes | Cost | Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                   |  114 |  3192 |  118 | 00:00:02 |
|   1 |   NESTED LOOPS                 |                   |      |       |      |          |
|   2 |    NESTED LOOPS                |                   |  114 |  3192 |  118 | 00:00:02 |
|   3 |     VIEW                       | index$_join$_001  |  115 |  1380 |    3 | 00:00:01 |
| * 4 |      HASH JOIN                 |                   |      |       |      |          |
|   5 |       INDEX FAST FULL SCAN     | EMP_DEPARTMENT_IX |  115 |  1380 |    1 | 00:00:01 |
|   6 |       INDEX FAST FULL SCAN     | EMP_NAME_IX       |  115 |  1380 |    1 | 00:00:01 |
| * 7 |     INDEX UNIQUE SCAN          | DEPT_ID_PK        |    1 |       |    0 | 00:00:01 |
|   8 |    TABLE ACCESS BY INDEX ROWID | DEPARTMENTS       |    1 |    16 |    1 | 00:00:01 |
---------------------------------------------------------------------------------------------

指定排序合并联接:

SELECT/*+ use_merge(departments ,employees ) */ last_name ,department_nameFROM employees  JOIN departments USING (department_id)WHERE department_id != 20;---------------------------------------------------------------------------------------------
| Id  | Operation                      | Name              | Rows | Bytes | Cost | Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                   |  113 |  3164 |    6 | 00:00:01 |
|   1 |   MERGE JOIN                   |                   |  113 |  3164 |    6 | 00:00:01 |
|   2 |    TABLE ACCESS BY INDEX ROWID | DEPARTMENTS       |   26 |   416 |    2 | 00:00:01 |
| * 3 |     INDEX FULL SCAN            | DEPT_ID_PK        |   26 |       |    1 | 00:00:01 |
| * 4 |    SORT JOIN                   |                   |  114 |  1368 |    4 | 00:00:01 |
|   5 |     VIEW                       | index$_join$_001  |  114 |  1368 |    3 | 00:00:01 |
| * 6 |      HASH JOIN                 |                   |      |       |      |          |
| * 7 |       INDEX FAST FULL SCAN     | EMP_DEPARTMENT_IX |  114 |  1368 |    1 | 00:00:01 |
|   8 |       INDEX FAST FULL SCAN     | EMP_NAME_IX       |  114 |  1368 |    1 | 00:00:01 |
---------------------------------------------------------------------------------------------

指定哈希散列接:

SELECT/*+ use_hash(employees,departments) */ *FROM employees  JOIN departments USING (department_id);-----------------------------------------------------------------------------
| Id  | Operation            | Name        | Rows | Bytes | Cost | Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |  114 | 10602 |    7 | 00:00:01 |
| * 1 |   HASH JOIN          |             |  114 | 10602 |    7 | 00:00:01 |
|   2 |    TABLE ACCESS FULL | DEPARTMENTS |   27 |   567 |    3 | 00:00:01 |
|   3 |    TABLE ACCESS FULL | EMPLOYEES   |  115 |  8280 |    3 | 00:00:01 |
-----------------------------------------------------------------------------

使用哈希散列的联接必须是等值条件的联接。散列联接最适合大量数据或一张表中大多数数据的情况。两张表中的较小表被优化器用来在联接键上创建散列列表,建立散列表。散列表会完全放在内存中。

(6) 在线应用升级提示

在线应用升级提示建议如何处理应用升级期间的insert 和update 冲突。
有三个提示:
CHANGE_DUPKEY_ERROR_INDEX
IGNORE_ROW_ON_DUPKEY_INDEX
RETRY_ON_ROW_CHANGE
使用CHANGE_DUPKEY_ERROR_INDEX和IGNORE_ROW_ON_DUPKEY_INDEX提示处理应用升级期间的INSERT操作冲突;
使用CHANGE_DUPKEY_ERROR_INDEX提示确认一个指定列集或索引的唯一键冲突。当插入或更新操作中遇到唯一键冲突时,将报ORA-38911错误,而不是ORA-001错误。
能使用IGNORE_ROW_ON_DUPKEY_INDEX提示对指定的列或索引忽略唯一键冲突,当单表插入操作发生唯一键冲突时,会执行行级别的回滚操作,然后继续下一行的操作。所以唯一键冲突不会导致插入的终止或者返回错误。
可以使用RETRY_ON_ROW_CHANGE提示处理在线应用升级期间UPDATE操作冲突。当行集合在被修改期间有变动,可以使用这个提示重试UPDATE或DELETE操作。

(7) 并行操作提示

并行操作提示指示优化器是否并行操作以及如何并行操作。可以使用下列并行提示:
PARALLEL 和 NO_PARALLEL
PARALLEL_INDEX 和 NO_PARALLEL_INDEX
PQ_DISTRIBUTE

控制并行度的提示

以关键字PARALLEL开头的提示表明查询的并行度。以NO_PARALLEL开头的提示禁用并行。
可以在语句级别或者对象级别指定并行度。如果没有在提示中显式的指定对象,则并行发生在语句级别。与多数提示不同,并行的语句级别提示优先于对象级提示。
为了阐明对象级别并行和语句级别并行的不同,可以执行以下步骤:
1.设置employees的并行度为2,并且禁用departments的并行:

ALTER TABLE employees PARALLEL 2;
ALTER TABLE departments NOPARALLEL;

2.执行sql语句:

SELECT /*+ PARALLEL(employees 3) */ e.last_name, d.department_name
FROM employees e, departments d
WHERE e.department_id=d.department_id;----------------------------------------------------------------------------------------------
| Id   | Operation                            | Name        | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------------------------------
|    0 | SELECT STATEMENT                     |             |  114 |  3192 |    5 | 00:00:01 |
|    1 |   PX COORDINATOR                     |             |      |       |      |          |
|    2 |    PX SEND QC (RANDOM)               | :TQ10001    |  114 |  3192 |    5 | 00:00:01 |
|    3 |     MERGE JOIN                       |             |  114 |  3192 |    5 | 00:00:01 |
|    4 |      SORT JOIN                       |             |   27 |   432 |    2 | 00:00:01 |
|    5 |       BUFFER SORT                    |             |      |       |      |          |
|    6 |        PX RECEIVE                    |             |   27 |   432 |    2 | 00:00:01 |
|    7 |         PX SEND BROADCAST            | :TQ10000    |   27 |   432 |    2 | 00:00:01 |
|    8 |          TABLE ACCESS BY INDEX ROWID | DEPARTMENTS |   27 |   432 |    2 | 00:00:01 |
|    9 |           INDEX FULL SCAN            | DEPT_ID_PK  |   27 |       |    1 | 00:00:01 |
| * 10 |      SORT JOIN                       |             |  115 |  1380 |    3 | 00:00:01 |
|   11 |       PX BLOCK ITERATOR              |             |  115 |  1380 |    2 | 00:00:01 |
|   12 |        TABLE ACCESS FULL             | EMPLOYEES   |  115 |  1380 |    2 | 00:00:01 |
----------------------------------------------------------------------------------------------

docs中给出的计划:

步骤2中的提示会覆盖表上的并行度。(不会深究并行操作的具体执行)

3.执行sql语句:

SELECT /*+ PARALLEL(4) */ hr_emp.last_name, d.department_name
FROM employees hr_emp, departments d
WHERE hr_emp.department_id=d.department_id;

因为并行提示中没有指定模式对象,所以提示的范围是语句,而不是对象。这个语句强制查询以4个并行度并行执行,并且覆盖表上定义的平行度设置。

控制负载分发方法的提示

PQ_DISTRIBUTE提示适用于平行的INSERT ... SELECT和 并行的CREATE TABLE AS SELECT语句指定行在生产者(查询)和消费者(加载)间如何分发。
例如:一个分区分发使用表的分区信息将分发的行从查询从属加载到加载从属。当遇到下列情况时使用这个方法:
1.将查询和加载操作合并到每个从属项中是不可能的,也是不可取的。
2.正在加载的分区数大于或等于加载从服务器的数量。
3.输入数据均匀地分布在正在加载的分区上。

(8) 查询转换的提示

下面的每个提示都指示优化器使用特定的SQL查询转换:
NO_QUERY_TRANSFORMATION
USE_CONCAT
NO_EXPAND
REWRITE 和 NO_REWRITE
MERGE 和 NO_MERGE
STAR_TRANSFORMATIOIN 和 NO_STAR_TRANSFORMATION
FACT 和 NO_FACT
UNNEST 和 NO_UNNEST

(9) 其他提示

还有下列提示:
APPEND,APPEND_VALUES和 NOAPPEND
CACHE 和 NOCACHE
PUSH_PRED 和 NO_PUSH_PRED
PUSH_SUBQ 和 NO_PUSH_SUBQ
QB_NAME
CURSOR_SHARING_EXACT
DRIVING_SITE
DYNAMIC_SAMPLING
MODEL_MIN_ANALYSIS

提示APPEND的示例:

指示数据库以直接路径加载的方式插入数据

CREATE TABLE tmp_objects AS SELECT * FROM all_objects WHERE 1=2;
INSERT  INTO tmp_objects
SELECT * FROM all_objects;INSERT  /*+append */ INTO tmp_objects
SELECT * FROM all_objects;

没有使用提示的插入语句计划如下:插入操作为LOAD TABLE CONVENTIONAL

--------------------------------------------------------------------------------------------------------------
| Id    | Operation                            | Name                    | Rows  | Bytes   | Cost | Time     |
--------------------------------------------------------------------------------------------------------------
|     0 | INSERT STATEMENT                     |                         | 31602 | 3855444 |  270 | 00:00:04 |
|     1 |   LOAD TABLE CONVENTIONAL            | TMP_OBJECTS             |       |         |      |          |
|   * 2 |    TABLE ACCESS BY INDEX ROWID       | SUM$                    |     1 |       9 |    1 | 00:00:01 |
|   * 3 |     INDEX UNIQUE SCAN                | I_SUM$_1                |     1 |         |    0 | 00:00:01 |
|     4 |    TABLE ACCESS BY INDEX ROWID       | OBJ$                    |     1 |      30 |    3 | 00:00:01 |

使用了append提示的插入语句计划如下:插入操作为LOAD AS SELECT

--------------------------------------------------------------------------------------------------------------
| Id    | Operation                            | Name                    | Rows  | Bytes   | Cost | Time     |
--------------------------------------------------------------------------------------------------------------
|     0 | INSERT STATEMENT                     |                         | 31602 | 3855444 |  270 | 00:00:04 |
|     1 |   LOAD AS SELECT                     | TMP_OBJECTS             |       |         |      |          |
|   * 2 |    TABLE ACCESS BY INDEX ROWID       | SUM$                    |     1 |       9 |    1 | 00:00:01 |
|   * 3 |     INDEX UNIQUE SCAN                | I_SUM$_1                |     1 |         |    0 | 00:00:01 |
|     4 |    TABLE ACCESS BY INDEX ROWID       | OBJ$                    |     1 |      30 |    3 | 00:00:01 |
|   * 5 |     INDEX RANGE SCAN                 | I_OBJ1                  |     1 |         |    2 | 00:00:01 |

append提示仅适合使用子查询进行插入的insert语句,不适合values子句进行的插入,对于values子句,使用append_values

INSERT INTO  tmp_objects(owner,object_name,object_id,created,last_ddl_time,namespace) VALUES
('l','l',0,DATE'2019-03-29',DATE'2019-03-29',0);
---------------------------------------------------------------------------------
| Id | Operation                 | Name        | Rows | Bytes | Cost | Time     |
---------------------------------------------------------------------------------
|  0 | INSERT STATEMENT          |             |    1 |       |    1 | 00:00:01 |
|  1 |   LOAD TABLE CONVENTIONAL | TMP_OBJECTS |      |       |      |          |
---------------------------------------------------------------------------------INSERT/*+ append_values*/ INTO  tmp_objects(owner,object_name,object_id,created,last_ddl_time,namespace) VALUES
('l','l',0,DATE'2019-03-29',DATE'2019-03-29',0);
-------------------------------------------------------------------------
| Id | Operation         | Name        | Rows | Bytes | Cost | Time     |
-------------------------------------------------------------------------
|  0 | INSERT STATEMENT  |             |    1 |       |    1 | 00:00:01 |
|  1 |   LOAD AS SELECT  | TMP_OBJECTS |      |       |      |          |
|  2 |    BULK BINDS GET |             |      |       |      |          |
-------------------------------------------------------------------------

2.特定的优化器提示

提示只使用于出现提示的语句块的优化。语句块要符合以下条件:
1.简单的SELECT 、UPDATE或者DELETE语句;
2.复杂语句的父语句或者子语句;
3.复合查询的一部分。

例如,两个查询块通过union操作组成一个组合查询语句,第一个查询块中的提示只适用于这个块的优化而不适用第二个块的优化。

2.1 指定一组完整的提示

使用提示时,某些情况下,可能需要指定一组完整的提示以确保选择执行计划。例如,有一个很多表联接组成的复杂查询,如果只指定给定一个表的索引,那么优化器必须决定其他要被使用的访问路径和联接方法。因此,即使你给定了index提示,优化器也不一定使用这个提示,因为优化器已经选择它使用的联接方法和访问路径而不使用指定的索引。

示例:一个指定了联接顺序和联接方法的查询

SELECT /*+ LEADING(e2 e1) USE_NL(e1) INDEX(e1 emp_emp_id_pk) USE_MERGE(j) FULL(j) */
e1.first_name, e1.last_name, j.job_id, sum(e2.salary) total_sal
FROM employees e1, employees e2, job_history j
WHERE e1.employee_id = e2.manager_id
AND e1.employee_id = j.employee_id
AND e1.hire_date = j.start_date
GROUP BY e1.first_name, e1.last_name, j.job_id
ORDER BY total_sal----------------------------------------------------------------------------------------------
| Id   | Operation                          | Name          | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------------------------------
|    0 | SELECT STATEMENT                   |               |  114 |  6384 |  125 | 00:00:02 |
|    1 |   SORT ORDER BY                    |               |  114 |  6384 |  125 | 00:00:02 |
|    2 |    HASH GROUP BY                   |               |  114 |  6384 |  125 | 00:00:02 |
|    3 |     MERGE JOIN                     |               |  114 |  6384 |  123 | 00:00:02 |
|    4 |      SORT JOIN                     |               |  114 |  3990 |  119 | 00:00:02 |
|    5 |       NESTED LOOPS                 |               |      |       |      |          |
|    6 |        NESTED LOOPS                |               |  114 |  3990 |  118 | 00:00:02 |
|    7 |         TABLE ACCESS FULL          | EMPLOYEES     |  115 |   920 |    3 | 00:00:01 |
|  * 8 |         INDEX UNIQUE SCAN          | EMP_EMP_ID_PK |    1 |       |    0 | 00:00:01 |
|    9 |        TABLE ACCESS BY INDEX ROWID | EMPLOYEES     |    1 |    27 |    1 | 00:00:01 |
| * 10 |      SORT JOIN                     |               |   10 |   210 |    4 | 00:00:01 |
|   11 |       TABLE ACCESS FULL            | JOB_HISTORY   |   10 |   210 |    3 | 00:00:01 |
----------------------------------------------------------------------------------------------

2.2 在提示中指定查询块

3.对视图使用提示

学习ORACLE-优化器提示(Hints)相关推荐

  1. 使用优化器提示(Optimizer Hints)

    优化器提示(Optimizer Hints)可以用在SQL语句中改变执行计划. 理解优化器提示 提示让你作出决定,这决定一般由优化器来作出.作为一个应用的设计者,你知道一些优化器不知道的关于你的数据的 ...

  2. MySQL 5.7-8.9.3 Optimizer Hints(优化器提示)

    One means of control over optimizer strategies is to set the optimizer_switch system variable (see S ...

  3. ORACLE优化器RBO与CBO介绍总结

    RBO和CBO的基本概念 Oracle数据库中的优化器又叫查询优化器(Query Optimizer).它是SQL分析和执行的优化工具,它负责生成.制定SQL的执行计划.Oracle的优化器有两种,基 ...

  4. Oracle优化器:星型转换(Star Query Transformation )

     Oracle优化器:星型转换(Star Query Transformation )  Star query是一个事实表(fact table)和一些维度表(dimension)的join.每个维度 ...

  5. 【Oracle 优化器】动态统计(Dynamic Statistics)

    概述 什么是动态统计Dynamic Statistics或者动态采样Dynamic Sampling 为什么要使用动态统计 动态统计都有哪些级别各个级别都有什么区别 如何确认采用了动态统计功能 dbm ...

  6. Oracle优化器:星型转换

    Oracle 8i中引入了星型转换(star transformation)的优化器新特性以便更有效地处理星型查询.星型查询语句多用于基于星型模型设计的数据仓库应用中.星型模型的称谓源于该种模型以图形 ...

  7. Oracle 优化器_表连接

    概述 在写SQL的时候,有时候涉及到的不仅只有一个表,这个时候,就需要表连接了.Oracle优化器处理SQL语句时,根据SQL语句,确定表的连接顺序(谁是驱动表,谁是被驱动表及 哪个表先和哪个表做链接 ...

  8. Oracle 优化器

    什么是优化器 优化器是Oracle中的一个核心模块,它的作用是为用户输入的SQL选择一个它计算出来的最高效的执行计划.SQL语句在Oracle中的执行过程如下图所示: 基于规则优化器RBO 基于规则的 ...

  9. PyTorch框架学习十三——优化器

    PyTorch框架学习十三--优化器 一.优化器 二.Optimizer类 1.基本属性 2.基本方法 三.学习率与动量 1.学习率learning rate 2.动量.冲量Momentum 四.十种 ...

最新文章

  1. Java 反射机制[Field反射]
  2. [html] html5中的meta标签robots有什么作用?
  3. 多线程知识梳理(2) - 并发编程的艺术笔记
  4. 空值用前值填充_用Excel进行数据处理
  5. Scrapy学习之第一个简单爬取小程序
  6. kernel oops_Java中的OOPS概念– OOPS概念示例
  7. UML/ROSE学习笔记系列一:建模原理、概念
  8. linux日志logger命令详解
  9. Thoughtworks笔试作业
  10. Vim跳转首行与尾行
  11. 耗时10个小时用纯HTML和CSS写成的小兔鲜儿
  12. 古月居 ROS 入门21讲--PA17 ROS中坐标系管理系统笔记
  13. java五大浏览器,[Java教程]各主流浏览器(PC、移动端)userAgent属性信息
  14. C语言 常量和宏定义
  15. 什么是Ntrip?Ntrip协议简介
  16. 众生百相:我的10年软件开发生涯
  17. 小优机器人系统恢复_解决一键还原ghost
  18. thinkphp实现用户注册时邮箱激活
  19. 乌班图linux分辨率不能调,ubuntu分辨率1024*768无法选择解决方法
  20. 网络信息安全之APT攻击

热门文章

  1. [Asp.net]web.config customErrors 如何设置?
  2. Oracle排序 将空值排在最后面
  3. k8s etcd 连接错误
  4. 方法分享给你!从零开始做自媒体,到实现月入过万我用了2个月
  5. Django的入门和模型的总结
  6. 为什么视频网站的视频链接地址是blob?
  7. Linux curl使用简单介绍
  8. 我仰望天空所看到的“云”
  9. 【Linux 统计文件行数】
  10. MATLAB如何让输出的图片的背景色为白色(方便文档编写)