When using hints, in some cases, you might need to specify a full set of hints in order to ensure the optimal execution plan. For example, if you have a very complex query, which consists of many table joins, and if you specify only the
INDEX
hint for a given table, then the optimizer needs to determine the remaining access paths to be used, as well as the corresponding join methods. Therefore, even though you gave the
INDEX
hint, the optimizer might not necessarily use that hint, because the optimizer might have determined that the requested index cannot be used due to the join methods and access paths selected by the optimizer.
WHERE ...
Unless the hints are inside the base view, they might not be honored from a query against the view.
Local Hints Compared with Global Hints
Table hints (in other words, hints that specify a table) generally refer to tables in the
DELETE
,
SELECT
, or
UPDATE
statement in which the hint occurs, not to tables inside any views referenced by the statement. When you want to specify hints for tables that appear inside views, Oracle recommends using global hints instead of embedding the hint in the view. Any table hint described in this chapter can be transformed into a global hint by using an extended syntax for the table name.
The hints described in this section let you choose between the cost-based and the rule-based optimization approaches. With the cost-based approach, this also includes the goal of best throughput or best response time.
For example, the optimizer uses the cost-based approach to optimize this statement for best throughput:
For example, the optimizer uses the cost-based approach to optimize this statement for best response time:
These statements cannot be optimized for best response time, because Oracle must retrieve all rows accessed by the statement before returning the first row. If you specify this hint in any of these statements, then the optimizer uses the cost-based approach and optimizes for best throughput.
If you specify either the
ALL_ROWS
or the
FIRST_ROWS
hint in a SQL statement, and if the data dictionary does not have statistics about tables accessed by the statement, then the optimizer uses default statistical values (such as allocated storage for such tables) to estimate the missing statistics and, subsequently, to choose an execution plan.
These estimates might not be as accurate as those gathered by the
DBMS_STATS
package. Therefore, use the
DBMS_STATS
package to gather statistics. If you specify hints for access paths or join operations along with either the
ALL_ROWS
or
FIRST_ROWS
hint, then the optimizer gives precedence to the access paths and join operations specified by the hints.
The
CHOOSE
hint causes the optimizer to choose between the rule-based and cost-based approaches for a SQL statement. The optimizer bases its selection on the presence of statistics for the tables accessed by the statement. If the data dictionary has statistics for at least one of these tables, then the optimizer uses the cost-based approach and optimizes with the goal of best throughput. If the data dictionary does not have statistics for these tables, then it uses the rule-based approach.
choose_hint::=
Text description of the illustration choose_hint.gif
WHERE employee_id = 7566;
Specifying one of these hints causes the optimizer to choose the specified access path only if the access path is available based on the existence of an index or cluster and on the syntactic constructs of the SQL statement. If a hint specifies an unavailable access path, then the optimizer ignores it.
You must specify the table to be accessed exactly as it appears in the statement. If the statement uses an alias for the table, then use the alias rather than the table name in the hint. The table name within the hint should not include the schema name if the schema name is present in the statement.
WHERE sex = 'm';
Assume that there is an index on the
SEX
column and that this column contains the values
m
and
f
. If there are equal numbers of male and female patients in the hospital, then the query returns a relatively large percentage of the table's rows, and a full table scan is likely to be faster than an index scan. However, if a very small percentage of the hospital's patients are male, then the query returns a relatively small percentage of the table's rows, and an index scan is likely to be faster than a full table scan.
Barring the use of frequency histograms, the number of occurrences of each distinct column value is not available to the optimizer. The cost-based approach assumes that each value has an equal probability of appearing in each row. For a column having only two distinct values, the optimizer assumes each value appears in 50% of the rows, so the cost-based approach is likely to choose a full table scan rather than an index scan.
If you know that the value in the
WHERE
clause of the query appears in a very small percentage of the rows, then you can use the
INDEX
hint to force the optimizer to choose an index scan. In this statement, the
INDEX
hint explicitly chooses an index scan on the
sex_index
, the index on the
sex
column:
WHERE sex = 'm';
The
INDEX
hint applies to
IN
-list predicates; it forces the optimizer to use the hinted index, if possible, for an
IN
-list predicate. Multicolumn
IN
-lists will not use an index.
INDEX_ASC
Because Oracle's default behavior for a range scan is to scan index entries in ascending order of their indexed values, this hint does not specify anything more than the
INDEX
hint. However, you might want to use the
INDEX_ASC
hint to specify ascending range scans explicitly should the default behavior change.
INDEX_COMBINE
The
INDEX_COMBINE
hint explicitly chooses a bitmap access path for the table. If no indexes are given as arguments for the
INDEX_COMBINE
hint, then the optimizer uses whatever Boolean combination of bitmap indexes has the best cost estimate for the table. If certain indexes are given as arguments, then the optimizer tries to use some Boolean combination of those particular bitmap indexes.
index_combine_hint::=
Text description of the illustration index_combine_hint.gif
The
NO_INDEX
hint applies to function-based, B-tree, bitmap, cluster, or domain indexes. If a
NO_INDEX
hint and an index hint (
INDEX
,
INDEX_ASC
,
INDEX_DESC
,
INDEX_COMBINE
, or
INDEX_FFS
) both specify the same indexes, then both the
NO_INDEX
hint and the index hint are ignored for the specified indexes and the optimizer considers the specified indexes.
For example:
WHERE employee_id > 200;
AND_EQUAL
The
REWRITE
hint forces the cost-based optimizer to rewrite a query in terms of materialized views, when possible, without cost consideration. Use the
REWRITE
hint with or without a view list. If you use
REWRITE
with a view list and the list contains an eligible materialized view, then 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, then Oracle searches for an eligible materialized view and always uses it regardless of its cost.
rewrite_hint::=
Text description of the illustration rewrite_hint.gif
FROM MV
NOREWRITE
If a view's query contains a
GROUP BY
clause or
DISTINCT
operator in the
SELECT
list, then the optimizer can merge the view's query 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.
Complex merging is not cost-based; that is, the accessing query block must include the
MERGE
hint. Without this hint, the optimizer uses another approach.
merge_hint::=
Text description of the illustration merge_hint.gif
WHERE e1.department_id = dallas_dept.department_id;
When the
NO_MERGE
hint is used without an argument, it should be placed in the view query block. When
NO_MERGE
is used with the view name as an argument, it should be placed in the surrounding query.
STAR_TRANSFORMATION
The
STAR_TRANSFORMATION
hint 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 only generates the subqueries if it seems reasonable to do so. If no subqueries are generated, then there is no transformed query, and the best plan for the untransformed query is used, regardless of the hint.
star_transformation_hint::=
Text description of the illustration star_transformation_hint.gif
Usually, if you analyze the tables, then the optimizer selects an efficient star plan. You can also use hints to improve the plan. The most precise method is to order the tables in the
FROM
clause in the order of the keys in the index, with the large table last. Then use the following hints:
/*+ ORDERED USE_NL(FACTS) INDEX(facts fact_concat) */
where
facts
is the table and
fact_concat
is the index. A more general method is to use the
STAR
hint.
Hints for Join Operations
In the hint you must specify a table exactly the same way it appears in the statement. If the statement uses an alias for the table, then you must use the alias rather than the table name in the hint. However, the table name within the hint should
not
include the schema name, if the schema name is present in the statement.
Use of the
USE_NL
and
USE_MERGE
hints is recommended with the
ORDERED
hint. Oracle uses these hints when the referenced table is forced to be the inner table of a join; the hints are ignored if the referenced table is the outer table.
USE_NL
For example, consider this statement, which joins the
accounts
and
customers
tables. Assume that these tables are not stored together in a cluster:
WHERE accounts.customer_id = customers.customer_id;
Because the default goal of the cost-based approach is best throughput, the optimizer chooses either a nested loops operation, a sort-merge operation, or a hash operation to join these tables, depending on which is likely to return all the rows selected by the query more quickly.
However, you might want to optimize the statement for best response time or the minimal elapsed time necessary to return the first row selected by the query, rather than best throughput. If so, then you can force the optimizer to choose a nested loops join by using the
USE_NL
hint. In this statement, the
USE_NL
hint explicitly chooses a nested loops join with the
customers
table as the inner table:
WHERE accounts.customer_id = customers.customer_id;
In many cases, a nested loops join returns the first row faster than a sort merge join. A nested loops join can return the first row after reading the first selected row from one table and the first matching row from the other and combining them, while a sort merge join cannot return the first row until after reading and sorting all selected rows of both tables and then combining the first rows of each sorted row source.
In the following statement where a nested loop is forced through a hint,
orders
is accessed through a full table scan and the filter condition
l.order_id = h.order_id
is applied to every row. For every row that meets the filter condition,
order_items
is accessed through the index
order_id
.
GROUP BY l2.product_id, l.order_date, l.order_id;
where
table
is the name or alias of a table to be used as the first table in the join order.
HASH_AJ, MERGE_AJ, and NL_AJ
Alternatively, the SQL predicate
NOT
IN
can be evaluated using an anti-join to subtract two sets. Thus,
employees
.
department_id
can be anti-joined to
departments
.
department_id
to select all employees who are not in a set of departments, and you can get a list of all employees who are not in the shipping or receiving departments.
HASH_SJ, MERGE_SJ, and NL_SJ
The
PUSH_SUBQ
hint causes non-merged subqueries to be evaluated 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 it improves performance to evaluate the subquery earlier.
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_hint::=
Text description of the illustration push_subq_hint.gif
WHERE cols > 3;
Using Hints with Views
Oracle does not encourage the use of hints inside or on views (or subqueries). This is because you can define views in one context and use them in another. However, such hints can result in unexpected execution plans. In particular, hints inside views or on views are handled differently, depending on whether the view is mergeable into the top-level query.
If you decide, nonetheless, to use hints with views, the following sections describe the behavior in each case.
If you want to specify a hint for a table in a view or subquery, then the global hint syntax is recommended. The following section describes this in detail.
Parallel Execution Hints on Views
PARALLEL
,
NOPARALLEL
,
PARALLEL_INDEX
, and
NOPARALLEL_INDEX
hints on views are applied recursively to all the tables in the referenced view. Parallel execution hints in a top-level query override such hints inside a referenced view.
Parallel Execution Hints Inside Views
PARALLEL
,
NOPARALLEL
,
PARALLEL_INDEX
, and
NOPARALLEL_INDEX
hints inside views are preserved when the view is merged with the top-level query. Parallel execution hints on the view in a top-level query override such hints inside a referenced view.
Hints and Nonmergeable Views
However, join hints on the view in the top-level query are preserved because, in this case, a nonmergeable view is similar to a table.
Global Hints
Table hints (hints that specify a table) normally refer to tables in the
DELETE
,
SELECT
, or
UPDATE
statement in which the hint occurs, not to tables inside any views referenced by the statement. When you want to specify hints for tables that appear inside views, use global hints instead of embedding the hint in the view. You can transform any table hint in this chapter into a global hint by using an extended syntax for the table name, described as follows.
Consider the following view definitions and
SELECT
statement:
WHERE department_id = 30;
The view
V1
retrieves all employees whose employee number is less than 150. The view
V2
performs a join between the view
V1
and the department table. The
SELECT
statement retrieves rows from the view
V2
restricting it to the department whose number is 30.
There are two global hints in the
SELECT
statement. The first hint specifies an index scan for the employee table referenced in the view
V1
, which is referenced in the view
V2
. The second hint specifies a full table scan for the department table referenced in the view
V2
. Note the dotted syntax for the view tables.
A hint such as: