Oracle Hints and Global Hints
from http://www.morganslibrary.org/reference/hints.html
General Information | ||||||||||||||||||||
Hint Sections |
|
|||||||||||||||||||
The demos on this page are intended to show valid syntax but it is far easier to use a hint to make a mess of things than it is to improve things. So many, if not most, of the demos here are written intentionally to show that they can increase the cost. Before you proceed read everything in this General Information section. Warning: Before you use this page, or any hint, it is critically important that you understand what you are about to do. The simple overriding fact is that Oracle Corp. has not documented the vast majority of hints and for those they have documented the documentation is wholly inadequate and often misleading. Some of the worst SQL I have ever seen has been bad not because it was terribly written from the standpoint of syntax but rather it was written by someone with less than a whole clue about how the hints incorporated into that SQL functioned. In an attempt to educate my readers I am providing this link to Jonathan Lewis' "Rules for Hinting" http://jonathanlewis.wordpress.com/2008/05/02/rules-for-hinting/ as well as the following quote from the page in case Jonathan ever does a house cleaning and removes it. Rules for Hinting
|
||||||||||||||||||||
Data Dictionary Objects |
|
|||||||||||||||||||
Dictionary Demo | col sql_feature format a24 col class format a25 col inverse format a30 col version format a8 SELECT class, name, sql_feature, inverse, version FROM gv$sql_hint ORDER BY 1,2; |
|||||||||||||||||||
Fully Hinting comment by Jonathan Lewis on USENET | Consider, for example: SELECT /*+ index(t1 t1_abc) index(t2 t2_abc) */ COUNT(*) FROM t1, t2 WHERE t1.col1 = t2.col1; For weeks, this may give you the plan: NESTED LOOP table access by rowid t1 index range scan t1_abc table access by rowid t2 index range scan t2_abc Then, because of changes in statistics, or init.ora parameters, or nullity of a column, or a few other situations that may have slipped my mind at the moment, this might change to HASH JOIN table access by rowid t2 index range scan t2_abc table access by rowid t1 index range scan t1_abc Your hints are still obeyed, the plan has changed. On the other hand, if you had specified SELECT /*+ no_parallel(t1) no_parallel(t2) no_parallel_index(t1) no_parallel_index(t2) ordered use_nl(t2) index(t1 t1_abc) index(t2 t2_abc) */ COUNT(*) FROM t1, t2 WHERE t1.col1 = t2.col1; Then I think you could be fairly confident that there was no way that Oracle could obey the hints whilst changing the access path. |
|||||||||||||||||||
Access Method Hints (Cluster) | ||||||||||||||||||||
Each following hints described in this section suggests an access method for a cluster. | ||||||||||||||||||||
CLUSTER | Explicitly requests a nested loop of the cluster index of one of the tables in the cluster. | |||||||||||||||||||
/* CLUSTER([@queryblock] <tablespec>) */ | ||||||||||||||||||||
conn uwclass/uwclass CREATE CLUSTER sc_srvr_id ( srvr_id NUMBER(10)) SIZE 1024; CREATE INDEX idx_sc_srvr_id ON CLUSTER sc_srvr_id; CREATE TABLE cservers CLUSTER sc_srvr_id (srvr_id) AS SELECT * FROM servers; CREATE TABLE cserv_inst CLUSTER sc_srvr_id (srvr_id) AS SELECT * FROM serv_inst; set autotrace traceonly exp SELECT cs.latitude FROM cservers cs, cserv_inst csi WHERE cs.srvr_id = csi.srvr_id; SELECT /*+ CLUSTER(cservers) */ cs.latitude FROM cservers cs, cserv_inst csi WHERE cs.srvr_id = csi.srvr_id; |
||||||||||||||||||||
HASH | Explicitly chooses a hash scan to access the specified table. Only applies to clusters. | |||||||||||||||||||
/*+ HASH(<tablespec>) */ | ||||||||||||||||||||
conn uwclass/uwclass CREATE CLUSTER sthc_si (srvr_id NUMBER(10)) SIZE 1024 SINGLE TABLE HASHKEYS 11 TABLESPACE uwdata; CREATE TABLE si_hash CLUSTER sthc_si (srvr_id) AS SELECT * FROM serv_inst; set autotrace traceonly explain SELECT srvr_id FROM si_hash WHERE srvr_id = 503 GROUP BY srvr_id; SELECT /*+ HASH(si_hash) */ srvr_id FROM si_hash WHERE srvr_id = 503 GROUP BY srvr_id; |
||||||||||||||||||||
Access Method Hints (Table) | ||||||||||||||||||||
Each following hints described in this section suggests an access method for a table. | ||||||||||||||||||||
BITMAP_TREE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
FULL | Explicitly chooses a full table scan for the specified table. | |||||||||||||||||||
/*+ FULL(<tablespec>) */ | ||||||||||||||||||||
conn uwclass/uwclass set autotrace traceonly explain SELECT s.latitude FROM servers s WHERE s.srvr_id = 1; SELECT /*+ FULL(servers) */ s.latitude FROM servers s WHERE s.srvr_id = 1; |
||||||||||||||||||||
INDEX | Explicitly chooses an index scan for the specified table. You can use the INDEX hint for domain, B*-tree, and bitmap indexes. However, Oracle recommends using INDEX_COMBINE rather than INDEX for bitmap indexes because it is a more versatile hint. | |||||||||||||||||||
/*+ INDEX([@queryblock] <tablespec> <index_name>) */ | ||||||||||||||||||||
conn oe/oe CREATE INDEX ix_customers_gender ON customers(gender); set autotrace traceonly explain SELECT * FROM customers c WHERE c.gender = 'M'; SELECT /*+ INDEX(customers ix_customers_gender) */ * FROM c.customers WHERE c.gender = 'M'; SELECT /*+ INDEX_ASC(customers ix_customers_gender) */ * FROM customers c WHERE c.gender = 'M'; SELECT /*+ INDEX_DESC(customers ix_customers_gender) */ * FROM customers c WHERE c.gender = 'M'; |
||||||||||||||||||||
INDEX_ASC | Explicitly chooses an index scan for the specified table. If the statement uses an index range scan, Oracle scans the index entries in ascending order of their indexed values. | |||||||||||||||||||
/*+ INDEX_ASC([@queryblock] <tablespec> <index_name>) */ | ||||||||||||||||||||
See INDEX Demo Above | ||||||||||||||||||||
INDEX_COMBINE | Explicitly chooses a bitmap access path for the table. If no indexes are given as arguments for the INDEX_COMBINE hint, the optimizer uses whatever Boolean combination of bitmap indexes has the best cost estimate for the table. If certain indexes are given as arguments, the optimizer tries to use some Boolean combination of those particular bitmap indexes. | |||||||||||||||||||
/*+ INDEX_DESC([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
conn hr/hr set autotrace traceonly explain SELECT * FROM employees e WHERE (e.manager_id = 108) OR (e.department_id = 110); SELECT /*+ INDEX_COMBINE(e emp_manager_ix emp_department_ix) */ * FROM employees e WHERE ((e.manager_id = 108) OR (e.department_id = 110)); |
||||||||||||||||||||
INDEX_DESC | Explicitly chooses an index scan for the specified table. If the statement uses an index range scan, Oracle scans the index entries in descending order of their indexed values. | |||||||||||||||||||
/*+ INDEX_DESC([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
See INDEX Demo Above | ||||||||||||||||||||
INDEX_FFS | Causes a fast full index scan rather than a full table scan. Appears to be identical to INDEX_FFS_ASC. | |||||||||||||||||||
/*+ INDEX_FFS([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
See INDEX SCAN Demos Below | ||||||||||||||||||||
INDEX_JOIN | Explicitly instructs the optimizer to use an index join as an access path. For the hint to have a positive effect, a sufficiently small number of indexes must exist that contain all the columns required to resolve the query. | |||||||||||||||||||
/*+ INDEX_JOIN([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
conn oe/oe set autotrace traceonly explain SELECT e.department_id FROM employees e WHERE e.manager_id < 110 AND e.department_id < 50; --------------------------------------------------------------------- | Id | Operation | Name | Cost(%CPU)| --------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 (0) | |* 1 | TABLE ACCESS BY INDEX ROWID | EMPLOYEES | 2 (0) | |* 2 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1 (0) | --------------------------------------------------------------------- SELECT /*+ INDEX_JOIN(e emp_manager_ix emp_department_ix) */ e.department_id FROM employees e WHERE e.manager_id < 110 AND e.department_id < 50; ------------------------------------------------------------- | Id | Operation | Name | Cost(%CPU) | ------------------------------------------------------------- | 0 | SELECT STATEMENT | | 3 (34) | |* 1 | VIEW | index$_join$_001 | 3 (34) | |* 2 | HASH JOIN | | | |* 3 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 2 (50) | |* 4 | INDEX RANGE SCAN | EMP_MANAGER_IX | 2 (50) | ------------------------------------------------------------- |
||||||||||||||||||||
INDEX_RRS | An internal hint that can only be inserted by the optimizer | |||||||||||||||||||
TBD | ||||||||||||||||||||
INDEX_RS | Instructs the optimizer to perform an index range scan for the specified table. | |||||||||||||||||||
/*+ INDEX_RS([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
INDEX_RS_ASC | Instructs the optimizer to perform an index range scan for the specified table. | |||||||||||||||||||
/*+ INDEX_RS([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
INDEX_RS_DESC | Instructs the optimizer to perform an index range scan for the specified table. | |||||||||||||||||||
/*+ INDEX_RS([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
conn oe/oe set autotrace traceonly explain SELECT c.customer_id FROM customers c WHERE c.customer_id BETWEEN 100 and 120; SELECT /*+ INDEX_RS_DESC(c customers_pk) */ c.customer_id FROM customers c WHERE c.customer_id BETWEEN 100 and 120; set autotrace off SELECT /*+ INDEX_RS_DESC(c customers_pk) */ c.customer_id FROM customers c WHERE c.customer_id BETWEEN 100 and 120; |
||||||||||||||||||||
INDEX_SS | Instructs the optimizer to perform an index skip scan for the specified table. If the statement uses an index range scan, then Oracle scans the index entries in ascending order of their indexed values. In a partitioned index, the results are in ascending order within each partition. | |||||||||||||||||||
/*+ INDEX_SS([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
See INDEX SCAN Demos Below | ||||||||||||||||||||
INDEX_SS_ASC | Instructs the optimizer to perform an index skip scan for the specified table. If the statement uses an index range scan, then Oracle Database scans the index entries in ascending order of their indexed values. In a partitioned index, the results are in ascending order within each partition. Each parameter serves the same purpose as in "INDEX Hint". The default behavior for a range scan is to scan index entries in ascending order of their indexed values, or in descending order for a descending index. This hint does not change the default order of the index, and therefore does not specify anything more than the INDEX_SS hint. However, you can use the INDEX_SS_ASC hint to specify ascending range scans explicitly should the default behavior change. |
|||||||||||||||||||
/*+ INDEX_SS_ASC([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
See INDEX SCAN Demos Below | ||||||||||||||||||||
INDEX_SS_DESC | Instructs the optimizer to perform an index skip scan for the specified table. If the statement uses an index range scan and the index is ascending, then Oracle scans the index entries in descending order of their indexed values. In a partitioned index, the results are in descending order within each partition. For a descending index, this hint effectively cancels out the descending order, resulting in a scan of the index entries in ascending order. | |||||||||||||||||||
/*+ INDEX_SS_DESC([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
See INDEX SCAN Demos Below | ||||||||||||||||||||
NLJ_BATCHING | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NLJ_PREFETCH | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_INDEX | Explicitly disallows a set of indexes for the specified table. The NO_INDEX hint applies to function-based, B*tree, bitmap, cluster, or domain indexes. | |||||||||||||||||||
/*+ NO_INDEX([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
conn uwclass/uwclass set autotrace traceonly explain SELECT s.latitude FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id; SELECT /*+ NO_INDEX(i ix_serv_inst) */ s.latitude FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id; SELECT /*+ NO_INDEX(i pk_serv_inst) */ s.latitude FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id; |
||||||||||||||||||||
NO_INDEX_FFS | Instructs the optimizer to exclude a fast full index scan of the specified indexes. | |||||||||||||||||||
/*+ NO_INDEX_FFS([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
conn uwclass/uwclass set autotrace traceonly exp SELECT s.latitude FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id; SELECT /*+ NO_INDEX_FFS(i pk_serv_inst) NO_INDEX_FFS(i ix_serv_inst) */ s.latitude FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id; |
||||||||||||||||||||
NO_INDEX_RS | Instructs the optimizer to exclude an index range scan of the specified indexes. | |||||||||||||||||||
/*+ NO_INDEX_RS([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
conn hr/hr col column_name format a30 SELECT uic.column_position, uic.column_name FROM user_ind_columns uic WHERE uic.index_name = 'EMP_NAME_IX'; set autotrace traceonly explain SELECT e.first_name FROM employees e WHERE e.last_name BETWEEN 'A' AND 'B'; SELECT /*+ NO_INDEX_RS(e emp_name_ix) */ e.last_name FROM employees e WHERE e.first_name BETWEEN 'A' AND 'B'; |
||||||||||||||||||||
NO_INDEX_SS | Instructs the optimizer to exclude a skip scan of the specified indexes. | |||||||||||||||||||
/*+ NO_INDEX_SS([@queryblock] <tablespec> <indexspec>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NO_NLJ_BATCHING | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_NLJ_PREFETCH | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
Index Scan Demos Also see the link at page bottom |
conn hr/hr col column_name format a30 SELECT uic.column_position, uic.column_name FROM user_ind_columns uic WHERE uic.index_name = 'EMP_NAME_IX'; set autotrace traceonly explain SELECT e.last_name FROM employees e; SELECT /*+ INDEX_FFS(e emp_name_ix) */ e.last_name FROM employees e; SELECT /*+ INDEX_SS(e emp_name_ix) */ e.last_name FROM employees e; SELECT /*+ INDEX_SS_ASC(e emp_name_ix) */ e.last_name FROM employees e; SELECT /*+ INDEX_DESC(e emp_name_ix) */ e.last_name FROM employees e; |
|||||||||||||||||||
Cache Hints | ||||||||||||||||||||
CACHE | Instructs the optimizer to place the blocks retrieved for the table at the most recently used end of the LRU list in the buffer cache when a full table scan is performed. This hint is useful for small lookup tables. | |||||||||||||||||||
/*+ CACHE([@queryblock] <tablespec>) */ | ||||||||||||||||||||
conn hr/hr set autotrace traceonly exp SELECT /*+ FULL (hr_emp) CACHE(hr_emp) */ last_name FROM employees hr_emp; SELECT /*+ FULL(hr_emp) NOCACHE(hr_emp) */ last_name FROM employees hr_emp; -- there is not change visible in the trace |
||||||||||||||||||||
CACHE_TEMP_TABLE | /*+ CACHE_TEMP_TABLE */ | |||||||||||||||||||
TBD | ||||||||||||||||||||
NOCACHE | Specifies that the blocks retrieved for this table are placed at the least recently used end of the LRU list in the buffer cache when a full table scan is performed. This is the normal behavior of blocks in the buffer cache. | |||||||||||||||||||
/*+ NOCACHE([@queryblock] <tablespec>]) */ | ||||||||||||||||||||
See CACHE Demo Above | ||||||||||||||||||||
General Purpose | ||||||||||||||||||||
DRIVING_SITE | Forces query execution to be done at a user selected site rather than at a site selected by the database. This hint is useful if you are using distributed query optimization. | |||||||||||||||||||
/*+ DRIVING_SITE([@queryblock] <tablespec>) */ | ||||||||||||||||||||
SELECT p1.first_name, p2.first_name, p2.last_name FROM person p1, person@mlib_user p2 WHERE p1.person_id = p2.person_id AND p1.first_name <> p2.first_name; SELECT /*+ DRIVING_SITE(p1) AAA */ p1.first_name, p2.first_name, p2.last_name FROM person p1, person@mlib_user p2 WHERE p1.person_id = p2.person_id AND p1.first_name <> p2.first_name; SELECT sql_text, remote FROM v$sql WHERE sql_text LIKE '%AAA%'; SELECT /*+ DRIVING_SITE(p2) BBB */ p1.first_name, p2.first_name, p2.last_name FROM person p1, person@mlib_user p2 WHERE p1.person_id = p2.person_id AND p1.first_name <> p2.first_name; SELECT sql_text, remote FROM v$sql WHERE sql_text LIKE '%BBB%'; |
||||||||||||||||||||
DYNAMIC_SAMPLING_EST_CDN | Force cardinality estimation for an analyzed tables | |||||||||||||||||||
SELECT /*+ dynamic_sampling(e 1) dynamic_sampling_est_cdn(e) */ COUNT(*) FROM hr.employees e WHERE e.commission_pct > 0.3; |
||||||||||||||||||||
MONITOR | Forces real-time SQL monitoring for the query, even if the statement is not long running. This hint is valid only when the parameter CONTROL_MANAGEMENT_PACK_ACCESS is set to DIAGNOSTIC+TUNING. | |||||||||||||||||||
/*+ MONITOR */ | ||||||||||||||||||||
SELECT value FROM v$parameter WHERE name = 'control_management_pack_access'; SELECT /*+ MONITOR */ COUNT(*) FROM user_tables; |
||||||||||||||||||||
NO_MONITOR | Disables real-time SQL monitoring for the query, even if the query is long running. | |||||||||||||||||||
/*+ NO_MONITOR */ | ||||||||||||||||||||
-- this SQL statement is made intentionally long running SELECT /*+ NO_MONITOR */ COUNT(*) FROM dba_segments s, dba_extents e WHERE s.owner = e.owner; |
||||||||||||||||||||
PX_JOIN_FILTER | Forces the optimizer to use parallel join bitmap filtering. | |||||||||||||||||||
/*+ PX_JOIN_FILTER(<tablespec>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NO_PX_JOIN_FILTER | Prevents the optimizer from using parallel join bitmap filtering. | |||||||||||||||||||
/*+ NO_PX_JOIN_FILTER(<tablespec>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NUM_INDEX_KEYS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
QB_NAME | Use the QB_NAME hint to define a name for a query block. This name can then be used in a hint in the outer query or even in a hint in an inline view to affect query execution on the tables appearing in the named query block. If two or more query blocks have the same name, or if the same query block is hinted twice with different names, then the optimizer ignores all the names and the hints referencing that query block. Query blocks that are not named using this hint have unique system-generated names. These names can be displayed in the plan table and can also be used in hints within the query block, or in query block hints. |
|||||||||||||||||||
/*+ QB_NAME(<query_block_name>) */ | ||||||||||||||||||||
conn hr/hr set autotrace traceonly exp SELECT employee_id, last_name FROM employees e WHERE last_name = 'Smith'; SELECT /*+ QB_NAME(qb) FULL(@qb e) */ employee_id, last_name FROM employees e WHERE last_name = 'Smith'; QB Demo: There is a very important lesson about the use of named query blocks well illustrated by a query posted in the OTN forums and Jonathan Lewis's response. First here's the query UPDATE ( SELECT /*+ QB_NAME(MAIN_EDR) USE_HASH(@SUBQ_MMT mmt0) LEADING (rec0 mmt0) INDEX_RS(@SUBQ_MMT mmt0 PSP_MONEY_MOVEMENT_TRANSAC_FK3) INDEX_RS(rec0 PSP_ENTRY_DETAIL_RECORD_I2 ) UNNEST(@SUBQ_MMT) MERGE(@MAIN_EDR) */ rec0.ENTRY_DETAIL_RECORD_SEQ, rec0.trace_number, rec0.N_A_C_H_A_FILE_FK, rec0.RECORD_DATA, rec0.VERSION, rec0.MODIFIER_ID, rec0.MODIFIED_DATE FROM PSP_ENTRY_DETAIL_RECORD rec0 WHERE rec0.N_A_C_H_A_FILE_FK IS NULL AND rec0.initiation_date = :p_offload_date AND rec0.N_A_C_H_A_FILE_TYPE = :p_nacha_file_type AND EXISTS ( SELECT /*+ qb_name(SUBQ_MMT) */ 'T' FROM PSP_MONEY_MOVEMENT_TRANSACTION mmt0 WHERE REC0.MONEY_MOVEMENT_TRANSACTION_FK = MMT0.MONEY_MOVEMENT_TRANSACTION_SEQ AND mmt0.offload_batch_fk = :p_offload_batch_id) ORDER BY rec0.LEGAL_NAME, rec0.COMPANY_fk, REC0.N_A_C_H_A_BATCH_TYPE, REC0.RECORD_DATA, rec0.AMOUNT, rec0.ENTRY_DETAIL_RECORD_SEQ) src SET trace_number = DECODE(NVL(record_data,'0'),'0', NULL, seq_trace_number.NEXTVAL), N_A_C_H_A_FILE_FK = :v_nacha_file_id, VERSION = VERSION + 1, MODIFIER_ID = :p_user_id, MODIFIED_DATE = :v_utc_date; Note how query_block SUBQ_MMT is names in the EXISTS subquery. The issue brought up in the forum was that the hint "was not working." Also posted was this: SQL> SELECT name, hint FROM user_outline_hints; NAME HINT ---- ---------- EDR USE_HASH(@"SEL$798CEC2A" "MMT0"@"SUBQ_MMT") EDR LEADING(@"SEL$798CEC2A" "REC0"@"MAIN_EDR" "MMT0"@"SUBQ_MMT") EDR INDEX_RS_ASC(@"SEL$798CEC2A" "MMT0"@"SUBQ_MMT" ("PSP_MONEY_MOVEMENT_TRANSACTI EDR INDEX_RS_ASC(@"SEL$798CEC2A" "REC0"@"MAIN_EDR" ("PSP_ENTRY_DETAIL_RECORD"."IN EDR OUTLINE(@"MAIN_EDR") EDR OUTLINE(@"UPD$1") EDR OUTLINE(@"SUBQ_MMT") EDR MERGE(@"MAIN_EDR") EDR OUTLINE(@"SEL$0E53DE07") EDR UNNEST(@"SUBQ_MMT") EDR OUTLINE_LEAF(@"SEL$798CEC2A") Here is Jonathan's response: You have two hints that are directed explicitly to a query block called subq_mmt - but after transformation that query block doesn't exist (so the hints are effectively meaningless), the query block SEL$798CEC2A has appeared instead from the unnesting and merging. The resulting query block is derived from the names of the query blocks that generated it, and is deterministic, so it's safe to use it in your own hints. So in your leading(), use_hash, and index_rs_asc hint you need to include @SEL$798CEC2A and then you should get the result you want. |
||||||||||||||||||||
RESULT_CACHE | Instructs the database to cache the results of the current query or query fragment in memory and then to use the cached results in future executions of the query or query fragment. The hint is recognized in the top-level query, the subquery_factoring_clause, or FROM clause inline view. The cached results reside in the result cache memory portion of the shared pool. | |||||||||||||||||||
/*+ RESULT_CACHE */ | ||||||||||||||||||||
SELECT /*+ RESULT_CACHE */ srvr_id FROM ( SELECT srvr_id, SUM(cnt) SUMCNT FROM ( SELECT DISTINCT srvr_id, 1 AS CNT FROM servers UNION ALL SELECT DISTINCT srvr_id, 1 FROM serv_inst) GROUP BY srvr_id) WHERE sumcnt = 2; |
||||||||||||||||||||
NO_RESULT_CACHE | The optimizer caches query results in the result cache if the RESULT_CACHE_MODE initialization parameter is set to FORCE. In this case, the NO_RESULT_CACHE hint disables such caching for the current query. | |||||||||||||||||||
/*+ NO_RESULT_CACHE */ | ||||||||||||||||||||
SELECT /*+ NO_RESULT_CACHE */ srvr_id FROM ( SELECT srvr_id, SUM(cnt) SUMCNT FROM ( SELECT DISTINCT srvr_id, 1 AS CNT FROM servers UNION ALL SELECT DISTINCT srvr_id, 1 FROM serv_inst) GROUP BY srvr_id) WHERE sumcnt = 2; |
||||||||||||||||||||
Join Operations Hints | ||||||||||||||||||||
Each hint described in this section suggests a table join operation. | ||||||||||||||||||||
Join methods: In the loop join algorithm, an outer loop is formed that is composed of a few entries that are to be selected. Then, for each entry in the outer loop, a look-up is performed for matching entries, in the inner loop. In the merge join algorithm, both tables are accessed in the same order. If there's a sorted index on the matching column, on both tables, then no sorting is needed. All we have to do is read the rows in the order presented by the index. The reason it's called a merge join is that the algorithm, in detail, looks much like the algorithm for merging two (sorted) data streams together. Let's say we got two tables, ORDERS and ORDER_ITEMS. Let's say we have sorted indexes on ORDER_NUMBER on both tables. Naturally, the index on ORDERS can forbid duplicates, while the index on ORDER_ITEMS has to permit duplicates. Now, in this case, which algorithm is faster? It depends. Let's say we want to look up a single order. This happens in OLTP systems a lot. The loop join is probably faster. The outer loop will find a single order number, and that means the inner loop will have to probe the index on ORDER_ITEMS just once. This is true even if we have to scan the order table, based on CUSTOMER_ID and ORDER_DATE. Now let's say we want a report for all the reports, with details for April. The merge join is probably faster. With hundreds of orders to process, walking the index on ORDER_ITEMS once beats the heck out of doing hundreds of probes. |
||||||||||||||||||||
NATIVE_FULL_OUTER_JOIN | Instructs the optimizer to use native full outer join, which is a native execution method based on a hash join. | |||||||||||||||||||
/*+ NATIVE_FULL_OUTER_JOIN */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NO_NATIVE_FULL_OUTER_JOIN | Instructs the optimizer to exclude the native execution method when joining each specified table. Instead, the full outer join is executed as a union of left outer join and anti-join. | |||||||||||||||||||
/*+ NO_NATIVE_FULL_OUTER_JOIN */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NO_USE_HASH | Instructs the optimizer to exclude hash joins when joining each specified table to another row source using the specified table as the inner table. | |||||||||||||||||||
/*+ NO_USE_HASH([@queryblock] <tablespec> <tablespec>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NO_USE_MERGE | Instructs the optimizer to exclude sort-merge joins when joining each specified table to another row source using the specified table as the inner table. | |||||||||||||||||||
/*+ NO_USE_MERGE([@queryblock] <tablespec> <tablespec>) */ | ||||||||||||||||||||
conn hr/hr set autotrace traceonly explain SELECT * FROM employees e, departments d WHERE e.department_id = d.department_id; SELECT /*+ NO_USE_MERGE(e d) */ * FROM employees e, departments d WHERE e.department_id = d.department_id; |
||||||||||||||||||||
NO_USE_NL | Instructs the optimizer to exclude nested loops joins when joining each specified table to another row source using the specified table as the inner table. | |||||||||||||||||||
/*+ NO_USE_NL([@queryblock] <tablespec> <tablespec>) */ | ||||||||||||||||||||
conn oe/oe set autotrace traceonly explain SELECT * FROM orders h, order_items l WHERE l.order_id = h.order_id AND l.order_id > 3500; SELECT /*+ NO_USE_NL(l h) */ * FROM orders h, order_items l WHERE l.order_id = h.order_id AND l.order_id > 3500; |
||||||||||||||||||||
USE_HASH | Causes Oracle to join each specified table with another row source with a hash join. | |||||||||||||||||||
/*+ USE_HASH([@queryblock] <tablespec> <tablespec>) */ | ||||||||||||||||||||
conn uwclass/uwclass set autotrace traceonly explain SELECT DISTINCT s.srvr_id FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id; SELECT /*+ USE_HASH (s i) */ DISTINCT s.srvr_id FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id; SELECT /*+ USE_MERGE (s i) */ DISTINCT s.srvr_id FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id; |
||||||||||||||||||||
USE_MERGE | Causes Oracle to join each specified table with another row source with a sort-merge join. | |||||||||||||||||||
/*+ USE_MERGE([@queryblock] <tablespec> <tablespec>) */ | ||||||||||||||||||||
See USE_HASH Demo Above | ||||||||||||||||||||
USE_MERGE_CARTESIAN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
USE_NL | Causes Oracle to join each specified table to another row source with a nested loops join using the specified table as the inner table. It stops the optimizer from evaluating the cost of a merge join or hash join - at a specific set of branch points in the CBO. | |||||||||||||||||||
/*+ USE_NL([@queryblock] <tablespec> <tablespec>) */ | ||||||||||||||||||||
conn uwclass/uwclass set autotrace traceonly explain SELECT DISTINCT s.srvr_id FROM servers s, serv_inst i WHERE s.srvr_id+0 = i.srvr_id+0; SELECT /*+ USE_NL(i s) */ DISTINCT s.srvr_id FROM servers s, serv_inst i WHERE s.srvr_id+0 = i.srvr_id+0; |
||||||||||||||||||||
USE_NL_WITH_INDEX | Instructs the optimizer to join the specified table to another row source with a nested loops join using the specified table as the inner table. | |||||||||||||||||||
/*+ USE_NL_WITH_INDEX([@queryblock] <tablespec> <index_name>) */ | ||||||||||||||||||||
conn oe/oe set autotrace traceonly explain SELECT * FROM orders h, order_items l WHERE l.order_id = h.order_id AND l.order_id > 3500; SELECT /*+ USE_NL_WITH_INDEX(l item_order_ix) */ * FROM orders h, order_items l WHERE l.order_id = h.order_id AND l.order_id > 3500; |
||||||||||||||||||||
Join Order Hints | ||||||||||||||||||||
The hints in this section suggest join orders: | ||||||||||||||||||||
LEADING | Instructs the optimizer to use the specified set of tables as the prefix in the execution plan. | |||||||||||||||||||
/*+ LEADING([@queryblock] <table_name> <table_name>) */ | ||||||||||||||||||||
conn hr/hr set autotrace traceonly explain SELECT * FROM employees e, departments d, job_history j WHERE e.department_id = d.department_id AND e.hire_date = j.start_date; SELECT /*+ LEADING(e j) */ * FROM employees e, departments d, job_history j WHERE e.department_id = d.department_id AND e.hire_date = j.start_date; |
||||||||||||||||||||
ORDERED | Causes Oracle to only join tables in the order in which they appear in the FROM clause. | |||||||||||||||||||
/*+ ORDERED */ | ||||||||||||||||||||
conn oe/oe set autotrace traceonly explain SELECT o.order_id, c.customer_id, l.unit_price * l.quantity FROM customers c, order_items l, orders o WHERE c.cust_last_name = 'Mastroianni' AND o.customer_id = c.customer_id AND o.order_id = l.order_id; SELECT /*+ ORDERED */ o.order_id, c.customer_id, l.unit_price * l.quantity FROM customers c, order_items l, orders o WHERE c.cust_last_name = 'Mastroianni' AND o.customer_id = c.customer_id AND o.order_id = l.order_id; |
||||||||||||||||||||
Miscellaneous Hints | ||||||||||||||||||||
APPEND | Instructs the optimizer to use direct-path INSERT if your database is running in serial mode. Your database is in serial mode if you are not using Enterprise Edition. Conventional INSERT is the default in serial mode, and direct-path INSERT is the default in parallel mode. In direct-path INSERT, data is appended to the end of the table, rather than using existing space currently allocated to the table. As a result, direct-path INSERT can be considerably faster than conventional INSERT. When you use the APPEND hint for INSERT, data is simply appended to a table above the HWM which has the effect of not creating UNDO. Existing free space in blocks is not used. |
|||||||||||||||||||
/*+ APPEND */ | ||||||||||||||||||||
conn uwclass/uwclass CREATE TABLE t AS SELECT * FROM servers WHERE 1=2; INSERT /*+ NO_APPEND */ INTO t SELECT * FROM servers; SELECT COUNT(*) FROM t; INSERT INTO t SELECT * FROM servers; SELECT COUNT(*) FROM t; INSERT /*+ APPEND */ INTO t SELECT * FROM servers; SELECT COUNT(*) FROM t; COMMIT; SELECT COUNT(*) FROM t; |
||||||||||||||||||||
APPEND_VALUES | Forces the optimizer to use direct-path INSERT with the VALUES clause. If you do not specify this hint, then conventional INSERT is used. This hint is only supported with the VALUES clause of the INSERT statement. If you specify it with an insert that uses the subquery syntax it is ignored. | |||||||||||||||||||
/*+ APPEND_VALUES */ | ||||||||||||||||||||
conn uwclass/uwclass CREATE TABLE t ( testcol VARCHAR2(3)); EXPLAIN PLAN FOR INSERT INTO t VALUES ('XYZ'); SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR INSERT /*+ APPEND_VALUES */ INTO t VALUES ('XYZ'); SELECT * FROM TABLE(dbms_xplan.display); |
||||||||||||||||||||
BYPASS_RECURSIVE_CHECK | See metalink for bug #1816154 | |||||||||||||||||||
TBD | ||||||||||||||||||||
CARDINALITY | Instructs the optimizer to use the provided integer as the computed cardinality of the table, or pipelined table function, without checking. | |||||||||||||||||||
/*+ CARDINALITY(<tablespec>, <integer>) */ | ||||||||||||||||||||
conn uwclass/uwclass set autotrace traceonly explain SELECT * FROM serv_inst si WHERE srvr_id = 1; SELECT /*+ CARDINALITY(si 999) */ * FROM serv_inst si WHERE srvr_id = 1; |
||||||||||||||||||||
conn hr/hr CREATE OR REPLACE TYPE employees_t AUTHID DEFINER AS OBJECT ( employee_id NUMBER(6), first_name VARCHAR2(20), last_name VARCHAR2(25), department_id NUMBER(4)); / CREATE OR REPLACE TYPE employees_t AUTHID DEFINER AS OBJECT ( employee_id NUMBER(6), first_name VARCHAR2(20), last_name VARCHAR2(25), department_id NUMBER(4)); / CREATE OR REPLACE TYPE employees_tt AS TABLE OF employees_t; / CREATE OR REPLACE FUNCTION employees_ptf RETURN employees_tt AUTHID CURRENT_USER PIPELINED IS BEGIN FOR r IN (SELECT employee_id, first_name, last_name, department_id FROM employees) LOOP FOR i IN 1 .. 200 LOOP PIPE ROW (employees_t(r.employee_id, r.first_name, r.last_name, r.department_id)); END LOOP; END LOOP; RETURN; END employees_ptf; / SELECT COUNT(*) FROM TABLE(employees_piped); EXPLAIN PLAN FOR SELECT * FROM departments d, TABLE(employees_piped) e WHERE d.department_id = e.department_id; SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT /*+ CARDINALITY(e, 21400) */ * FROM departments d, TABLE(employees_piped) e WHERE d.department_id = e.department_id; SELECT * FROM TABLE(dbms_xplan.display); |
||||||||||||||||||||
NO_APPEND | Forces the optimizer to use conventional INSERT by disabling parallel for the INSERT. | |||||||||||||||||||
/*+ NO_APPEND */ | ||||||||||||||||||||
See APPEND Demo Above | ||||||||||||||||||||
NO_CPU_COSTING | Turn off CPU Costing | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_MONITORING | Undocumented but possibly an updated syntax for NO_MONITOR | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_PUSH_JOIN_PRED | Prevent pushing of a join predicate into the view | |||||||||||||||||||
/*+ NO_PUSH_JOIN_PRED (<tablespec>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NO_QKN_BUFF | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_USE_INVISIBLE_INDEXES | /*+ NO_USE_INVISIBLE_INDEXES */ | |||||||||||||||||||
CREATE TABLE invis AS SELECT table_name, tablespace_name FROM all_tables; CREATE INDEX ix_invis ON invis(table_name) INVISIBLE; set autotrace on ALTER SESSION SET "optimizer_use_invisible_indexes" = TRUE; SELECT table_name FROM invis WHERE table_name = 'SERVERS'; SELECT /*+ NO_USE_INVISIBLE_INDEXES */ table_name FROM invis WHERE table_name = 'SERVERS'; |
||||||||||||||||||||
PRESERVE_OID | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
PUSH_JOIN_PRED | Force pushing of a join predicate into the view (found in the 8.1.5 docs) | |||||||||||||||||||
/*+ PUSH_JOIN_PRED (<tablespec>) */ | ||||||||||||||||||||
SELECT /*+ PUSH_JOIN_PRED(v) */ T1.X, V.Y FROM T1 ( SELECT T2.X, T3.Y FROM T2, T3 WHERE T2.X = T3.X) v WHERE t1.x = v.x AND t1.y = 1; |
||||||||||||||||||||
SEMIJOIN_DRIVER | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SWAP_JOIN_INPUTS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
USE_INVISIBLE_INDEXES | /*+ USE_INVISIBLE_INDEXES */ | |||||||||||||||||||
CREATE TABLE invis AS SELECT table_name, tablespace_name FROM all_tables; CREATE INDEX ix_invis ON invis(table_name) INVISIBLE; set autotrace on SELECT table_name FROM invis WHERE table_name = 'SERVERS'; SELECT /*+ USE_INVISIBLE_INDEXES */ table_name FROM invis WHERE table_name = 'SERVERS'; |
||||||||||||||||||||
Model Clause | ||||||||||||||||||||
MODEL_COMPILE_SUBQUERY | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
MODEL_DONTVERIFY_UNIQUENESS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
MODEL_DYNAMIC_SUBQUERY | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
MODEL_MIN_ANALYSIS | Instructs the optimizer to omit some compile-time optimizations of spreadsheet rules—primarily detailed dependency graph analysis. Other spreadsheet optimizations, such as creating filters to selectively populate spreadsheet access structures and limited rule pruning, are still used by the optimizer. This hint reduces compilation time because spreadsheet analysis can be lengthy if the number of spreadsheet rules is more than several hundreds. |
|||||||||||||||||||
/*+ MODEL_MIN_ANALYSIS */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
MODEL_NO_ANALYSIS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
MODEL_PUSH_REF | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_MODEL_PUSH_REF | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
Optimization Approaches and Goals Hints | ||||||||||||||||||||
Mode hints were introduced in version 8.1.0 and none have related inverse hints. | ||||||||||||||||||||
ALL_ROWS | The ALL_ROWS hint explicitly chooses the cost-based approach to optimize a statement block with a goal of best throughput (that is, minimum total resource consumption). | |||||||||||||||||||
/*+ ALL_ROWS */ | ||||||||||||||||||||
conn / as sysdba set linesize 121 col name format a30 col value format a30 SELECT name, value FROM gv$parameter WHERE name LIKE '%optimizer%'; ALTER SYSTEM SET optimizer_mode=RULE SCOPE=MEMORY; set autotrace traceonly explain SELECT table_name FROM dba_tables WHERE owner = 'SYS' AND table_name LIKE '%$' ORDER BY 1; SELECT /*+ ALL_ROWS */ table_name FROM dba_tables WHERE owner = 'SYS' AND table_name LIKE '%$' ORDER BY 1; ALTER SYSTEM SET optimizer_mode=ALL_ROWS SCOPE=MEMORY; |
||||||||||||||||||||
CHOOSE | /*+ CHOOSE */ | |||||||||||||||||||
TBD | ||||||||||||||||||||
FIRST_ROWS(n) | The FIRST_ROWS hint explicitly chooses the cost-based approach to optimize a statement block with a goal of best response time (minimum resource usage to return first row). This hint causes the optimizer to make these choices:
|
|||||||||||||||||||
/*+ FIRST_ROWS(<integer>) */ | ||||||||||||||||||||
set autotrace trace exp SELECT table_name FROM dba_tables WHERE owner = 'SYS' AND table_name LIKE '%$' ORDER BY 1; SELECT /*+ FIRST_ROWS(10) */ table_name FROM dba_tables WHERE owner = 'SYS' AND table_name LIKE '%$' ORDER BY 1; -- the differences are subtle so look closely |
||||||||||||||||||||
RULE | Disables the use of the optimizer. This hint is not supported and should not be used. | |||||||||||||||||||
/*+ RULE */ | ||||||||||||||||||||
set autotrace trace exp SELECT table_name FROM dba_tables WHERE owner = 'SYS' AND table_name LIKE '%$' ORDER BY 1; SELECT /*+ RULE */ table_name FROM dba_tables WHERE owner = 'SYS' AND table_name LIKE '%$' ORDER BY 1; |
||||||||||||||||||||
Optimizer Feature Enabling Hints | ||||||||||||||||||||
CURSOR_SHARING_EXACT | Oracle can replace literals in SQL statements with bind variables, when it is safe to do so. This replacement is controlled with the CURSOR_SHARING initialization parameter. The CURSOR_SHARING_EXACT hint instructs the optimizer to switch this behavior off. In other words, Oracle executes the SQL statement without any attempt to replace literals with bind variables. | |||||||||||||||||||
/*+ CURSOR_SHARING_EXACT */ | ||||||||||||||||||||
conn / as sysdba ALTER SYSTEM SET cursor_sharing='SIMILAR' SCOPE=BOTH; ALTER SYSTEM FLUSH SHARED_POOL; ALTER SYSTEM FLUSH SHARED_POOL; -- as the client run two similar SQL statements SELECT latitude FROM uwclass.servers WHERE srvr_id = 1; SELECT latitude FROM uwclass.servers WHERE srvr_id = 2; SELECT latitude FROM uwclass.servers WHERE srvr_id = 3; -- as SYS look in the shared pool set linesize 121 col sql_text format a50 SELECT address, child_address, sql_text, sql_id FROM gv$sql WHERE sql_fulltext LIKE '%uwclass%'; SELECT /*+ CURSOR_SHARING_EXACT */ latitude FROM uwclass.servers WHERE srvr_id = 3; SELECT address, child_address, sql_text, sql_id FROM gv$sql WHERE sql_fulltext LIKE '%uwclass%'; |
||||||||||||||||||||
DYNAMIC_SAMPLING | The DYNAMIC_SAMPLING hint instructs the optimizer how to control dynamic sampling to improve server performance by determining more accurate predicate selectivity and statistics for tables and indexes. You can set the value of DYNAMIC_SAMPLING to a value from 0 to 10. The higher the level, the more effort the compiler puts into dynamic sampling and the more broadly it is applied. Sampling defaults to cursor level unless you specify tablespec. The integer value is 0 to 10, indicating the degree of sampling. For pipelined table functions the range of values is 2 to 10. Force dynamic sampling of tables where statistics do not exist such as Global Temporary Tables. If the table is aliased the alias name, not the table name must be used. |
|||||||||||||||||||
/*+ DYNAMIC_SAMPLING([@queryblock] [<tablespec>] <integer>) */ | ||||||||||||||||||||
conn uwclass/uwclass CREATE TABLE ds AS SELECT * FROM all_objects WHERE SUBSTR(object_name,1,1) BETWEEN 'A' AND 'W'; CREATE INDEX ds_objtype ON ds(object_type); SELECT object_type, COUNT(*) FROM ds GROUP BY object_type; set autotrace trace exp SELECT object_name FROM ds WHERE object_type = 'JAVA CLASS'; SELECT /*+ DYNAMIC_SAMPLING(ds 0) */ object_name FROM ds WHERE object_type = 'JAVA CLASS'; SELECT /*+ DYNAMIC_SAMPLING(ds 4) */ object_name FROM ds WHERE object_type = 'JAVA CLASS'; SELECT /*+ DYNAMIC_SAMPLING(ds 9) */ object_name FROM ds WHERE object_type = 'JAVA CLASS'; |
||||||||||||||||||||
OPTIMIZER_FEATURES_ENABLE | Acts as an umbrella parameter for enabling a series of optimizer features based on an Oracle Database release number. It can be used tocheck for plan regressions after database upgrades. Specify the release number as an argument to the hint. | |||||||||||||||||||
/*+ optimizer_features_enable('<version_number>') */ | ||||||||||||||||||||
SELECT /*+ OPTIMIZER_FEATURE_ENABLE('11.1.0.7') */ latitude, longitude FROM uwclass.servers WHERE srvr_id = 5; |
||||||||||||||||||||
OPT_ESTIMATE | Alter, to optimize, the CBO's default scaling factor. | |||||||||||||||||||
/*+ OPT_ESTIMATE(table, <alias>, scale_rows=<scaling_factor>) */ | ||||||||||||||||||||
conn hr/hr CREATE OR REPLACE TYPE employees_t AUTHID DEFINER AS OBJECT ( employee_id NUMBER(6), first_name VARCHAR2(20), last_name VARCHAR2(25), department_id NUMBER(4)); / CREATE OR REPLACE TYPE employees_tt AS TABLE OF employees_t; / CREATE OR REPLACE FUNCTION employees_ptf RETURN employees_tt AUTHID CURRENT_USER PIPELINED IS BEGIN FOR r IN (SELECT employee_id, first_name, last_name, department_id FROM employees) LOOP FOR i IN 1 .. 200 LOOP PIPE ROW (employees_t(r.employee_id, r.first_name, r.last_name, r.department_id)); END LOOP; END LOOP; RETURN; END employees_ptf; / SELECT COUNT(*) FROM TABLE(employees_ptf); EXPLAIN PLAN FOR SELECT * FROM departments d, TABLE(employees_ptf) e WHERE d.department_id = e.department_id; SELECT * FROM TABLE(dbms_xplan.display); -- calculate the scaling factor SELECT 21400/8168 FROM dual; EXPLAIN PLAN FOR SELECT /*+ OPT_ESTIMATE(table, e, scale_rows=2.62) */ * FROM departments d, TABLE(employees_ptf) e WHERE d.department_id = e.department_id; SELECT * FROM TABLE(dbms_xplan.display); |
||||||||||||||||||||
OPT_PARAM | Lets you set an initialization parameter for the duration of the current query only. This hint is valid only for the following parameters: OPTIMIZER_DYNAMIC_SAMPLING, OPTIMIZER_INDEX_CACHING, OPTIMIZER_INDEX_COST_ADJ, OPTIMIZER_SECURE_VIEW_MERGING, and STAR_TRANSFORMATION_ENABLED. For example, the following hint sets the parameter STAR_TRANSFORMATION_ENABLED to TRUE for the statement to which it is added. | |||||||||||||||||||
/*+ OPT_PARAM(parameter_name, parameter_value) */ | ||||||||||||||||||||
SELECT name, value FROM v$parameter WHERE name LIKE 'optimizer_index%'; SELECT /*+ OPT_PARAM('optimizer_index_cost_adj' '42') */ * FROM servers; |
||||||||||||||||||||
Parallel Execution Hints | ||||||||||||||||||||
The NOPARALLEL and SHARED were introduced in 8.1.0. NO_PARALLEL was introduced in 10.1.0.3 as a replacement for NOPARALLEL to confirm with the hint naming convention. The hints described in this section determine how statements are parallelized or not parallelized when using parallel execution. | ||||||||||||||||||||
NO_PARALLEL and NOPARALLEL | Overrides a PARALLEL specification in the table clause. In general, hints take precedence over table clauses. | |||||||||||||||||||
/*+ NO_PARALLEL([@queryblock] <tablespec>) */ | ||||||||||||||||||||
conn hr/hr CREATE TABLE employees_demo PARALLEL (DEGREE 4) AS SELECT * FROM employees; SELECT table_name, degree FROM user_tables; set autotrace traceonly exp SELECT last_name FROM employees_demo hr_emp; SELECT /*+ NO_PARALLEL(hr_emp) */ last_name FROM employees_demo hr_emp; |
||||||||||||||||||||
NO_PARALLEL_INDEX | Override a PARALLEL attribute setting on an index. In this way you can avoid a parallel index scan operation. | |||||||||||||||||||
/*+ NO_PARALLEL_INDEX([@queryblock] <tablespec> <index_name>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
PARALLEL | Specifies the desired number of concurrent servers that can be used for a parallel operation. The hint applies to the INSERT, UPDATE, and DELETE portions of a statement as well as to the table scan portion. If any parallel restrictions are violated, the hint is ignored. The hint will (roughly speaking) make the optimizer divide a critical value by the value specified – at a specific set of points in the CBO. | |||||||||||||||||||
/*+ PARALLEL([@queryblock] <tablespec> <degree | DEFAULT>) */ | ||||||||||||||||||||
conn hr/hr set autotrace traceonly exp SELECT last_name FROM employees hr_emp; SELECT /*+ FULL(hr_emp) PARALLEL(hr_emp, 2) */ last_name FROM employees hr_emp; -- overrides table definition and uses init parmameter SELECT /*+ FULL(hr_emp) PARALLEL(hr_emp, DEFAULT) */ last_name FROM employees hr_emp; |
||||||||||||||||||||
PARALLEL_INDEX | Specify the desired number of concurrent servers that can be used to parallelize index range scans for partitioned indexes. | |||||||||||||||||||
/*+ PARALLEL_INDEX([@queryblock] <tablespec> <index_name> <degree | DEFAULT>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
PQ_DISTRIBUTE | Improve parallel join operation performance. Do this by specifying how rows of joined tables should be distributed among producer and consumer query servers. Using this hint overrides decisions the optimizer would normally make. Outer_distribution is the distribution for the outer table. Inner_distribution is the distribution for the inner table. Distribution values are: NONE, PARTITION, RANDOM, RANDOM_LOCAL |
|||||||||||||||||||
/*+ PQ_DISTRIBUTE([@queryblock] <tablespec> <distribution>) */ /*+ PQ_DISTRIBUTE([@queryblock] <tablespec> <outer_distribution><inner_distribution>) */ |
||||||||||||||||||||
conn uwclass/uwclass CREATE /*+ PQ_DISTRIBUTE(airplanes_hashpart, PARTITION) */ TABLE airplanes_hashpart NOLOGGING PARALLEL 16 PARTITION BY HASH (program_id) PARTITIONS 11 AS SELECT * FROM airplanes; |
||||||||||||||||||||
Query Rewrite Hints | ||||||||||||||||||||
The rewrite hints were introduced in two batches. REWRITE and NO_REWRITE in 8.1.5 and the remainder in 10.1.0.3. | ||||||||||||||||||||
CHECK_ACL_REWRITE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_BASETABLE_MULTIMV_REWRITE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_MULTIMV_REWRITE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_PULL_PRED | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_PUSH_PRED | Instructs the optimizer not to push a join predicate into the view. | |||||||||||||||||||
/*+ NO_PUSH_PRED(<@queryblock> | <[@queryblock> <tablespec>]) */ | ||||||||||||||||||||
conn hr/hr set autotrace traceonly exp SELECT * FROM employees e, ( SELECT manager_id FROM employees) v WHERE e.manager_id = v.manager_id(+) AND e.employee_id = 100; SELECT /*+ NO_MERGE(v) NO_PUSH_PRED(v) */ * FROM employees e, ( SELECT manager_id FROM employees) v WHERE e.manager_id = v.manager_id(+) AND e.employee_id = 100; |
||||||||||||||||||||
NO_REWRITE | Use on any query block of a request. This hint disables query rewrite for the query block, overriding the setting of the parameter QUERY_REWRITE_ENABLED. | |||||||||||||||||||
/*+ NO_REWRITE(<@queryblock>) */ | ||||||||||||||||||||
conn sh/sh set autotrace traceonly explain SELECT SUM(s.amount_sold) AS dollars FROM sales s, times t WHERE s.time_id = t.time_id GROUP BY t.calendar_month_desc; SELECT /*+ NO_REWRITE */ SUM(s.amount_sold) AS dollars FROM sales s, times t WHERE s.time_id = t.time_id GROUP BY t.calendar_month_desc; |
||||||||||||||||||||
OLD_PUSH_PRED | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
OR_EXPAND | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
PULL_PRED | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
PUSH_PRED | Instructs the optimizer to push a join predicate into the view. | |||||||||||||||||||
/*+ PUSH_PRED(<@queryblock> | <[@queryblock> <tablespec>]) */ | ||||||||||||||||||||
conn hr/hr set autotrace trace exp SELECT * FROM employees e, ( SELECT manager_id FROM employees) v WHERE e.manager_id = v.manager_id(+) AND e.employee_id = 100; SELECT /*+ NO_MERGE(v) PUSH_PRED(v) */ * FROM employees e, ( SELECT manager_id FROM employees) v WHERE e.manager_id = v.manager_id(+) AND e.employee_id = 100; |
||||||||||||||||||||
REWRITE | Use with or without a view list. If you use REWRITE with a view list and the list contains an eligible materialized view, Oracle uses that view regardless of its cost. Oracle does not consider views outside of the list. If you do not specify a view list, Oracle searches for an eligible materialized view and always uses it regardless of its cost. | |||||||||||||||||||
/*+ REWRITE([@queryblock] <view, view, ...>) */ | ||||||||||||||||||||
conn uwclass/uwclass CREATE MATERIALIZED VIEW mv_rewrite TABLESPACE uwdata REFRESH ON DEMAND ENABLE QUERY REWRITE AS SELECT s.srvr_id, i.installstatus, COUNT(*) FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id GROUP BY s.srvr_id, i.installstatus; set autotrace traceonly exp SELECT s.srvr_id, i.installstatus, COUNT(*) FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id AND s.srvr_id = 502 GROUP BY s.srvr_id, i.installstatus; SELECT /*+ REWRITE */ s.srvr_id, i.installstatus, COUNT(*) FROM servers s, serv_inst i WHERE s.srvr_id = i.srvr_id AND s.srvr_id = 502 GROUP BY s.srvr_id, i.installstatus; |
||||||||||||||||||||
REWRITE_OR_ERROR | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
Query Transformation Hints | ||||||||||||||||||||
CONNECT_BY_COST_BASED | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
FACT | In the context of the star transformation instructs the optimizer that the table specified be considered a fact table. | |||||||||||||||||||
/*+ FACT([@queryblock] <tablespec>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
GBY_CONC_ROLLUP | Likely related to GROUP BY | |||||||||||||||||||
TBD | ||||||||||||||||||||
INLINE | If you want to control the optimiser, then the 'materialize' hint makes it create a temporary table; the 'inline' hint makes it perform 'macro-substitution'. ~ Jonathan Lewis | |||||||||||||||||||
TBD | ||||||||||||||||||||
LIKE_EXPAND | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
MATERIALIZE | If you want to control the optimiser, then the 'materialize' hint makes it create a temporary table; the 'inline' hint makes it perform 'macro-substitution'. ~ Jonathan Lewis | |||||||||||||||||||
TBD | ||||||||||||||||||||
MERGE | The MERGE hint lets you merge views in a query. If a view's query block contains a GROUP BY clause or DISTINCT operator in the SELECT list, then the optimizer can merge the view into the accessing statement only if complex view merging is enabled. Complex merging can also be used to merge an IN subquery into the accessing statement if the subquery is uncorrelated. | |||||||||||||||||||
/*+ MERGE(<@queryblock> [tablespec]); */ | ||||||||||||||||||||
conn hr/hr set autotrace traceonly exp SELECT e1.last_name, e1.salary, v.avg_salary FROM employees e1, ( SELECT department_id, AVG(salary) avg_salary FROM employees e2 GROUP BY department_id) v WHERE e1.department_id = v.department_id AND e1.salary > v.avg_salary SELECT /*+ MERGE(v) */ e1.last_name, e1.salary, v.avg_salary FROM employees e1, ( SELECT department_id, AVG(salary) avg_salary FROM employees e2 GROUP BY department_id) v WHERE e1.department_id = v.department_id AND e1.salary > v.avg_salary; |
||||||||||||||||||||
MV_MERGE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_CHECK_ACL_REWRITE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_CONNECT_BY_COST_BASED | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_EXPAND | Prevents the cost-based optimizer from considering OR-expansion for queries having OR conditions or INLISTS in the WHERE clause. Normally, the optimizer would consider using OR expansion and use this method if it decides the cost is lower than not using it. | |||||||||||||||||||
/*+ NO_EXPAND(<@queryblock>); | ||||||||||||||||||||
conn oe/oe set autotrace traceonly explain SELECT * FROM employees e, departments d WHERE e.manager_id = 108 OR d.department_id = 110; SELECT /*+ NO_EXPAND */ * FROM employees e, departments d WHERE e.manager_id = 108 OR d.department_id = 110; |
||||||||||||||||||||
NO_EXPAND_GSET_TO_UNION | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_FACT | Used in the context of the star transformation. Instruct the optimizer that the queried table should not be considered as a fact table. | |||||||||||||||||||
/*+ NO_FACT([@queryblock] <tablespec>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NO_MERGE | Instructs the optimizer not to combine the outer query and any inline view queries into a single query. This hint makes the optimizer bypass some code that would otherwise transform a query using the rules of complex view merging. | |||||||||||||||||||
/*+ NO_MERGE(<@queryblock> [tablespecification]); */ | ||||||||||||||||||||
conn hr/hr set autotrace traceonly explain SELECT e1.last_name, seattle_dept.department_name FROM employees e1, (SELECT location_id, department_id, department_name FROM departments WHERE location_id = 1700) seattle_dept WHERE e1.department_id = seattle_dept.department_id; SELECT /*+ NO_MERGE(seattle_dept) */ e1.last_name, seattle_dept.department_name FROM employees e1, ( SELECT location_id, department_id, department_name FROM departments WHERE location_id = 1700) seattle_dept WHERE e1.department_id = seattle_dept.department_id; |
||||||||||||||||||||
NO_ORDER_ROLLUPS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_PRUNE_GSETS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_PUSH_SUBQ | Instructs the optimizer to evaluate nonmerged subqueries as the last step in the execution plan. Doing so can improve performance if the subquery is relatively expensive or does not reduce the number of rows significantly. | |||||||||||||||||||
/*+ NO_PUSH_SUBQ(<@queryblock>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NO_QUERY_TRANSFORMATION | Instructs the optimizer to skip all query transformations, including but not limited to OR-expansion, view merging, subquery unnesting, star transformation, and materialized view rewrite. | |||||||||||||||||||
/*+ NO_QUERY_TRANSFORMATION) */ | ||||||||||||||||||||
conn uwclass/uwclass set autotrace traceonly explain SELECT DISTINCT srvr_id FROM servers WHERE srvr_id NOT IN ( SELECT srvr_id FROM servers MINUS SELECT srvr_id FROM serv_inst); SELECT /*+ NO_QUERY_TRANSFORMATION */ DISTINCT srvr_id FROM servers WHERE srvr_id NOT IN ( SELECT srvr_id FROM servers MINUS SELECT srvr_id FROM serv_inst); |
||||||||||||||||||||
NO_STAR_TRANSFORMATION | Instructs the optimizer not to perform star query transformation. | |||||||||||||||||||
/*+ NO_STAR_TRANSFORMATION(<@queryblock>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NO_UNNEST | Turns off subqueries unnesting for the current statement. | |||||||||||||||||||
/*+ NO_UNNEST(<@queryblock>) */ | ||||||||||||||||||||
conn uwclass/uwclass set autotrace traceonly explain SELECT srvr_id FROM servers WHERE srvr_id IN ( SELECT /*+ UNNEST */ srvr_id FROM serv_inst); ------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU) | ------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 11 | 5 (20) | |* 1 | HASH JOIN SEMI | | 11 | 5 (20) | | 2 | INDEX FULL SCAN | PK_SERVERS | 141 | 1 (0) | | 3 | INDEX FAST FULL SCAN | PK_SERVERS | 999 | 3 (0) | ------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("SRVR_ID"="SRVR_ID") SELECT srvr_id FROM servers WHERE srvr_id IN ( SELECT /*+ NO_UNNEST */ srvr_id FROM serv_inst); --------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU) | --------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 128 (0) | |* 1 | INDEX FULL SCAN | PK_SERVERS | 7 | 1 (0) | |* 2 | INDEX FULL SCAN | PK_SERV_INST | 2 | 2 (0) | --------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "SERV_INST" "SERV_INST" WHERE "SRVR_ID"=:B1)) 2 - access("SRVR_ID"=:B1) filter("SRVR_ID"=:B1) |
||||||||||||||||||||
OPAQUE_TRANSFORM | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
OPAQUE_XCANONICAL | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
PRECOMPUTE_SUBQUERY | Tanel Poder's blog entry on this hint is the best on the web so here's the link. Below I have just copied from Tanel an example of the proper usage of this hint from his blog. [Click Here] | |||||||||||||||||||
SELECT a FROM t1 WHERE a IN ( SELECT /*+ PRECOMPUTE_SUBQUERY */ b FROM t2); |
||||||||||||||||||||
PUSH_SUBQ | Instructs the optimizer to evaluate nonmerged subqueries at the earliest possible step in the execution plan. Generally, subqueries that are not merged are executed as the last step in the execution plan. If the subquery is relatively inexpensive and reduces the number of rows significantly, then evaluating the subquery earlier can improve performance. This hint has no effect if the subquery is applied to a remote table or one that is joined using a merge join. |
|||||||||||||||||||
/*+ PUSH_SUBQ(<@queryblock>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
STAR_TRANSFORMATION | Makes the optimizer use the best plan in which the transformation has been used. Without the hint, the optimizer could make a cost-based decision to use the best plan generated without the transformation, instead of the best plan for the transformed query. Even if the hint is given, there is no guarantee that the transformation will take place. The optimizer will only generate the subqueries if it seems reasonable to do so. If no subqueries are generated, there is no transformed query, and the best plan for the untransformed query will be used regardless of the hint. |
|||||||||||||||||||
/*+ STAR_TRANSFORMATION(<@queryblock>) */ | ||||||||||||||||||||
conn sh/sh set autotrace traceonly exp SELECT * FROM sales s, times t, products p, channels c WHERE s.time_id = t.time_id AND s.prod_id = p.prod_id AND s.channel_id = c.channel_id AND p.prod_status = 'obsolete'; SELECT /*+ STAR_TRANSFORMATION */ * FROM sales s, times t, products p, channels c WHERE s.time_id = t.time_id AND s.prod_id = p.prod_id AND s.channel_id = c.channel_id AND p.prod_status = 'obsolete'; |
||||||||||||||||||||
UNNEST | Instructs the optimizer to unnest and merge the body of the subquery into the body of the query block that contains it, allowing the optimizer to consider them together when evaluating access paths and joins. | |||||||||||||||||||
/*+ UNNEST(<@queryblock>) */ | ||||||||||||||||||||
See NO_UNNEST Demo Above | ||||||||||||||||||||
USE_CONCAT | Forces combined OR conditions in the WHERE clause of a query to be transformed into a compound query using the UNION ALL set operator. Normally, this transformation occurs only if the cost of the query using the concatenations is cheaper than the cost without them. The USE_CONCAT hint turns off inlist processing and OR-expands all disjunctions, including inlists. |
|||||||||||||||||||
/*+ USE_CONCAT */ | ||||||||||||||||||||
conn hr/hr set autotrace traceonly explain SELECT * FROM employees e WHERE manager_id = 108 OR department_id = 110; SELECT /*+ USE_CONCAT */ * FROM employees e WHERE manager_id = 108 OR department_id = 110; |
||||||||||||||||||||
USE_TTT_FOR_GSETS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
Semantic-Effect Hints also known as Online Application Upgrade Hints | ||||||||||||||||||||
note | ||||||||||||||||||||
CHANGE_DUPKEY_ERROR_INDEX | This hint provides a mechanism to unambiguously identify a unique key violation for a specified set of columns or for a specified index. When a unique key violation occurs for the specified index, an ORA-38911 error is reported instead of an ORA-001. | |||||||||||||||||||
CHANGE_DUPKEY_ERROR_INDEX(<table_name>,<index_name>); CHANGE_DUPKEY_ERROR_INDEX(<table_name>,(<column_name>,<column_name>)); |
||||||||||||||||||||
CREATE TABLE t ( testcol VARCHAR2(20)); ALTER TABLE t ADD CONSTRAINT uc_t_testcol UNIQUE (testcol); INSERT INTO t (testcol) VALUES ('A'); INSERT INTO t (testcol) VALUES ('A'); INSERT /*+ CHANGE_DUPKEY_ERROR_INDEX(T,TESTCOL) */ INTO t (testcol) VALUES ('A'); |
||||||||||||||||||||
IGNORE_ROW_ON_DUPKEY_INDEX | This hint applies only to single-table INSERT operations. It causes the statement to ignore a unique key violation for a specified set of columns or for a specified index. When a unique key violation is encountered, a row-level rollback occurs and execution resumes with the next input row. If you specify this hint when inserting data with DML error logging enabled, then the unique key violation is not logged and does not cause statement termination. | |||||||||||||||||||
IGNORE_ROW_ON_DUPKEY_INDEX(table, index); IGNORE_ROW_ON_DUPKEY_INDEX(table, (column_list)); |
||||||||||||||||||||
CREATE TABLE t ( testcol NUMBER(2)); ALTER TABLE t ADD CONSTRAINT uc_t_testcol UNIQUE (testcol); INSERT INTO t (testcol) VALUES (1); INSERT INTO t (testcol) VALUES (7); INSERT INTO t (testcol) VALUES (1); INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(T,UC_T_TESTCOL)) */ INTO t (testcol) VALUES (1); INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(T,(TESTCOL)) */ INTO t (testcol) VALUES (1); INSERT INTO t (testcol) VALUES (1); SELECT * FROM t; BEGIN FOR i IN 1..10 LOOP INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(T,(TESTCOL)) */ INTO t (testcol) VALUES (i); END LOOP; END; / SELECT * FROM t; |
||||||||||||||||||||
RETRY_ON_ROW_CHANGE This demo is a modified version of Rob van Wijk. I wrote my own demo, not much different from Rob's. But the truth is that Rob's is better so I have made a few small changes but essentially what you see to the right is what Rob wrote. I encourage you to, as I do, follow his blog. |
This hint is valid only for UPDATE and DELETE operations. It is not supported for INSERT or MERGE. When specified the operation is retried when the ORA_ROWSCN for one or more rows in the set has changed from the time the set of rows to be modified is determined to the time the block is actually modified. | |||||||||||||||||||
-- demo setup CREATE TABLE t ( rid NUMBER(5), col NUMBER(5) NOT NULL); ALTER TABLE t ADD CONSTRAINT pk_t PRIMARY KEY (rid); INSERT /*+ APPEND */ INTO t SELECT level, level FROM dual CONNECT BY LEVEL <= 50000; CREATE TABLE logtable ( last_id NUMBER(5), number_of_processed_ids NUMBER(5)); INSERT INTO logtable VALUES (NULL,0); CREATE OR REPLACE PROCEDURE p(pid IN t.rid%TYPE) AUTHID DEFINER IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN UPDATE logtable SET last_id = pid, number_of_processed_ids = number_of_processed_ids + 1; COMMIT; END p; / CREATE OR REPLACE TRIGGER aut_t AFTER UPDATE ON T FOR EACH ROW BEGIN p(:NEW.rid); END aut_t; / |
||||||||||||||||||||
|
||||||||||||||||||||
Semijoin Hints | ||||||||||||||||||||
The first semijoin hint, NL_SJ, was introduced in 8.0.0, HASH_SJ and MERGE_SJ in 8.1.0, and NO_SEMI_JOIN and SEMIJOIN in 9.0.0. | ||||||||||||||||||||
MERGE_SJ | Transforms a correlated EXISTS subquery into a merge semi-join to access the specified table. | |||||||||||||||||||
/*+ MERGE_SJ (<tablespec>) */ | ||||||||||||||||||||
TBD | ||||||||||||||||||||
NO_SEMIJOIN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SEMIJOIN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
USE_SEMI | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
Stats Hints | ||||||||||||||||||||
All three of these hints were introduced in 10.1.0.3. | ||||||||||||||||||||
COLUMN_STATS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
INDEX_STATS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
TABLE_STATS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
XML and XMLINDEX_REWRITE Hints | ||||||||||||||||||||
COST_XML_QUERY_REWRITE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
FORCE_XML_QUERY_REWRITE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
INLINE_XMLTYPE_NT | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_COST_XML_QUERY_REWRITE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_XML_DML_REWRITE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_XML_QUERY_REWRITE | Instructs the optimizer to prohibit the rewriting of XPath expressions in SQL statements. | |||||||||||||||||||
/*+ NO_XML_QUERY_REWRITE */ | ||||||||||||||||||||
SELECT /*+NO_XML_QUERY_REWRITE*/ XMLQUERY('<A/>') FROM dual; |
||||||||||||||||||||
NO_XMLINDEX_REWRITE | Instructs the optimizer to prohibit the rewriting of XPath expressions in SQL statements. | |||||||||||||||||||
/*+ NO_XMLINDEX_REWRITE */ | ||||||||||||||||||||
SELECT /*+ NO_XMLINDEX_REWRITE */ COUNT(*) FROM t WHERE existsNode(OBJECT_VALUE, '/*') = 1; |
||||||||||||||||||||
NO_XMLINDEX_REWRITE_IN_SELECT | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
XML_DML_RWT_STMT | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
XMLINDEX_REWRITE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
XMLINDEX_REWRITE_IN_SELECT | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
Undocumented Optimizer Hints | ||||||||||||||||||||
Found while doing research ... some of these may not exist in 11gR2 | ||||||||||||||||||||
ANTIJOIN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
BIND_AWARE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
BITMAP | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
BUFFER | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
BYPASS_UJVC | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
CACHE_CB | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
CONNECT_BY_COMBINE_SW | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
CONNECT_BY_FILTERING | Undocumented but found in an AWR report as shown | |||||||||||||||||||
SELECT /*+ connect_by_filtering */ privilege#, level FROM sysauth$ CONNECT BY grantee#=prior privilege# AND privilege#>0 START WITH grantee#=:1 AND privilege#>0; |
||||||||||||||||||||
CPU_COSTING | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
CUBE_GB | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
DBMS_STATS | DBMS_STATS | |||||||||||||||||||
According to comments by Jonathan Lewis this not an Oracle hint but rather explanatory text put in by some developers to label code and make it easier to identify the statement as being generated by DBMS_STATS. | ||||||||||||||||||||
DB_VERSION First Available:11.1.0.6 |
Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
DEREF_NO_REWRITE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
DML_UPDATE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
DOMAIN_INDEX_FILTER | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
DOMAIN_INDEX_NO_SORT | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
DOMAIN_INDEX_SORT | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
ELIMINATE_JOIN First Available 10.2.0.1 |
Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
ELIMINATE_OBY | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
EXPR_CORR_CHECK | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
FBTSCAN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
GATHER_PLAN_STATISTICS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
GBY_PUSHDOWN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
HWM_BROKERED | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
IGNORE_OPTIM_EMBEDDED_HINTS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
IGNORE_WHERE_CLAUSE | The following does not work so this hint is not as obvious as it might, at first, seem. Not a big surprise. | |||||||||||||||||||
SELECT /*+ IGNORE_WHERE_CLAUSE */ COUNT(*) FROM servers WHERE srvr_id LIKE '%5%' |
||||||||||||||||||||
INCLUDE_VERSION | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
LOCAL_INDEXES | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
MERGE_CONST_ON | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NESTED_TABLE_FAST_INSERT | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NESTED_TABLE_GET_REFS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NESTED_TABLE_SET_SETID | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NOCPU_COSTING | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_ACCESS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_BIND_AWARE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_BUFFER | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_CONNECT_BY_COMBINE_SW | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_CONNECT_BY_FILTERING | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_PARTIAL_COMMIT | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_QKN_BUFF | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_REF_CASCADE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_CARTESIAN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_DOMAIN_INDEX_FILTER | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_ELIMINATE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_ELIMINATE_JOIN | Undocumented | |||||||||||||||||||
SELECT /*+ gather_plan_statistics optimizer_features_enable('10.2.0.4') */ * FROM ( SELECT /*+ qb_name(v2) */ raw_sql_.business_entity_id, raw_sql_.business_entity_name, raw_sql_.owner_id, raw_sql_.address_id, rownum raw_rnum_ FROM ( SELECT /*+ qb_name(v1) NO_ELIMINATE_JOIN(b1) */ * FROM businessentities b1 WHERE (b1.business_entity_id IN ( SELECT /*+ qb_name(in) */ b2.business_entity_id FROM businessentities b2 WHERE (business_entity_id = 'OWNER') OR (owner_id = 'ALL_IN_ALL') AND business_entity_id NOT IN ( SELECT /*+ qb_name(not_in) */ r.business_entity_id FROM busentityroles r))) WHERE rownum <= 5 ORDER BY business_entity_id asc) raw_sql_) WHERE raw_rnum_ > 0; |
||||||||||||||||||||
NO_ELIMINATE_OBY | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_GBY_PUSHDOWN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_LOAD | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_OUTER_JOIN_TO_INNER | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_PLACE_GROUP_BY | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_SET_TO_JOIN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_SQL_TUNE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_STATS_GSETS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_SUBQUERY_PRUNING | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_SWAP_JOIN_INPUTS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
NO_USE_HASH_AGGREGATION | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
OB_NAME | ob_name(my_view) | |||||||||||||||||||
TBD | ||||||||||||||||||||
OUTER_JOIN_TO_INNER | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
OUTLINE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
OUTLINE_LEAF | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
OVERFLOW_NOMOVE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
PIV_GB | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
PIV_SSF | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
PLACE_GROUP_BY | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
PQ_MAP | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
PQ_NOMAP | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
QUEUE_CURR | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
QUEUE_ROWP | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
RBO_OUTLINE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
REMOTE_MAPPED | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
RESTORE_AS_INTERVALS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
RESTRICT_ALL_REF_CONS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
REF_CASCADE_CURSOR | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
RESTORE_AS_INTERVALS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SAVE_AS_INTERVALS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SCN_ASCENDING Metalink Note 6688108.8 |
Causes Oracle to ignore ORA-01555 which is truly frightening as noted at the link below | |||||||||||||||||||
Documented Here by Oak Table Member and Oracle ACE Randolph Geist | ||||||||||||||||||||
SEMIJOIN_DRIVER | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SET_TO_JOIN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SKIP_EXT_OPTIMIZER | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SKIP_UNQ_UNUSABLE_IDX | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SQLLDR | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
STREAMS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SUBQUERY_PRUNING | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SWAP_JOIN_INPUTS | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SYS_DL_CURSOR | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SYS_PARALLEL_TXN | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
SYS_RID_ORDER | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
TIV_GB | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
TIV_SSF | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
TRACING | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
USE_ANTI | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
USE_HASH_AGGREGATION | Reported to have no affect during DML statements | |||||||||||||||||||
TBD | ||||||||||||||||||||
USE_WEAK_NAME_RESL | Use weak name resolution. | |||||||||||||||||||
TBD | ||||||||||||||||||||
VECTOR_READ | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
VECTOR_READ_TRACE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
X_DYN_PRUNE | Undocumented | |||||||||||||||||||
TBD | ||||||||||||||||||||
Global Hints | ||||||||||||||||||||
Global Hints Demo | conn hr/hr CREATE OR REPLACE VIEW v AS SELECT e1.first_name, e1.last_name, j.job_id, SUM(e2.salary) total_sal FROM employees e1, (SELECT * FROM employees e3) 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 AND e1.salary = ( SELECT MAX(e2.salary) FROM employees e2 WHERE e2.department_id = e1.department_id) GROUP BY e1.first_name, e1.last_name, j.job_id ORDER BY total_sal; EXPLAIN PLAN FOR SELECT * FROM v; SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT /*+ INDEX(v.e2.e3 emp_job_ix) */ * FROM v; SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT /*+ INDEX(@SEL$2 e2.e3 emp_job_ix) */ * FROM v; SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT /*+ INDEX(@SEL$3 e3 emp_job_ix) */ * FROM v; SELECT * FROM TABLE(dbms_xplan.display); |
|||||||||||||||||||
Global Hints with NO_MERGE Demo | conn hr/hr CREATE OR REPLACE VIEW v1 AS SELECT * FROM employees WHERE employee_id < 150; CREATE OR REPLACE VIEW v2 AS SELECT v1.employee_id employee_id, departments.department_id department_id FROM v1, departments WHERE v1.department_id = departments.department_id; EXPLAIN PLAN FOR SELECT * FROM v2 WHERE department_id = 30; SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT /*+ NO_MERGE(v2) INDEX(v2.v1.employees emp_emp_id_pk) FULL(v2.departments) */ * FROM v2 WHERE department_id = 30; SELECT * FROM TABLE(dbms_xplan.display); |
小小菜鸟一枚