You are on page 1of 6

Database

HINTS ON HINTING
Jonathan Lewis, JL Computer Consultancy

THE

PROBLEM WITH HINTS

Hints give us the opportunity to direct the optimizer away from the path it would take by
default, and there are several kinds of hints to help us. Some hints are strategic, some are for
feature control, some are for micro management, some are used to adjust the optimizers
arithmetic and some control run-time behavior.
The trouble with hints, though, is that they are badly documented and very few people really
know how to use them well. Consequently they are badly understood and it is very easy to
make mistakes while hinting. Because of this, I generally advise people to avoid hinting in
production systems and, if they do decide that hints are necessary, to stick to just the feature
and strategic hints. In this document, though, I want to give you some ideas about how hints
really work, what some of the more popular ones mean, and how to minimize the risk of using
them.

ARE

HINTS HINTS

The name hint is a bad one. An Oracle hint is an order to the optimizer (or, just
occasionally, to the run-time engine) that must be obeyed if it is legal.
There are reasons, though, why Oracle may appear to ignore hints. When using a hint you may
have made a simple spelling or syntax error (and the correct syntax can change with Oracle
version), you could be using the hint in a context where it is meaningless, you may have built
several contradictory hints into a statement with the result that Oracle ignores all the hints
which are not self-consistent and, finally, there are some Oracle bugs which result in hints
disappearing. But if you can get your hints right, Oracle must obey them.
One of the more interesting context reasons for Oracle ignoring your hints is the use of the
parameter (new to 10g):
alter session set _optimizer_ignore_hints = true;

(You could also do this at the system level or in the parameter file). If you have a lot of time for
testing when you upgrade Oracle versions, heres an interesting exercise to consider: how
about doing a test run with all hints disabled to find out which statements no longer need
hinting and then remove the redundant hints from the code. (I believe that hints in sysrecursive SQL are not ignored when this parameter is set to true, and stored outlines and SQL
profiles will still work. but that belief is based on just a couple of quick tests.)
But if hints are really orders that cannot be ignored, why do some many people think that
Oracle can ignore them? The answer is that we dont have the information we need to
understand exactly what even the simplest hints mean. Consider, for example, the simple
parallel hint.
I have a table which is list-partitioned on a column called pt_group, with a local index on
columns (n1, n2). I put a parallel hint into a particular query, and the optimizer appears to
ignore it:
select
/*+ parallel(t1,2) */
{list of columns}
1

Session #239

Database

from
t1
where
and
and
;

t1.n1 = 5
t1.n2 = 10
t1.pt_group in (0,10)

.
|Id|Operation
|Name |Cost |Pstart| Pstop|
| 0|SELECT STATEMENT
|
|
48|
|
|
| 1| PARTITION LIST INLIST
|
|
48|KEY(I)|KEY(I)|
|*2| TABLE ACCESS BY LOCAL INDEX ROWID|T1
|
48|KEY(I)|KEY(I)|
|*3|
INDEX RANGE SCAN
|T1_I1|
3|KEY(I)|KEY(I)|

Has the optimizer ignored my hint? The answer is no, and we could prove that by examining
the 10053 trace file. However, its rarely necessary to turn to the 10053 trace file, when a
couple of simple tests will show us whats going on.
Notice that the query is expected to run serially using an inlist iterator to access some
partitions through the local index take a quick look at the cost (48) of this access path, and
then add a hint to the query to block the use of any indexes on the table:
select
/*+ full(t1) parallel(t1,2) */
{list of columns}
from
t1
where
and
and
;

t1.n1 = 5
t1.n2 = 10
t1.pt_group in (0,10)

| Id| Operation
|Name |Cost
| 0| SELECT STATEMENT
|
| 94
| 1| PX COORDINATOR
|
|
| 2|
PX SEND QC (RANDOM)|:TQ10| 94
| 3|
PX BLOCK ITERATOR |
| 94
|* 4|
TABLE ACCESS FULL|T1
| 94

.
|Pstart|Pstop | TQ |IN-OUT|PQ Distrib|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|Q1,00| P->S |QC (RAND) |
|KEY(I)|KEY(I)|Q1,00| PCWC |
|
|KEY(I)|KEY(I)|Q1,00| PCWP |
|

With the addition of the full(t1) hint we get a full tablescan (after all, Oracle has to obey the
hint), and the tablescan should run as a parallel tablescan. But notice that the cost has gone up
to 94.
Now lets take out the full(t1) hint, and change the hinted degree of parallelism from two to
four:
select
/*+ parallel(t1,4) */
{list of columns}
from
t1
where
and
and

t1.n1 = 5
t1.n2 = 10
t1.pt_group in (0,10)
2

Session #239

Database

;
| Id| Operation
|Name |Cost
| 0| SELECT STATEMENT
|
| 47
| 1| PX COORDINATOR
|
|
| 2|
PX SEND QC (RANDOM)|:TQ10| 47
| 3|
PX BLOCK ITERATOR |
| 47
|* 4|
TABLE ACCESS FULL|T1
| 47

.
|Pstart|Pstop | TQ |IN-OUT|PQ Distrib|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|Q1,00| P->S |QC (RAND) |
|KEY(I)|KEY(I)|Q1,00| PCWC |
|
|KEY(I)|KEY(I)|Q1,00| PCWP |
|

Again, we see a tablescan appearing unhinted that Oracle expects to run in parallel. Notice,
though, that the cost of this path is now 47 just a little bit less than the cost of the serial
index access path.
The hint parallel(t1, N) does not tell Oracle to run the query in parallel at degree N it tells the
optimizer that when it is calculating the cost of a tablescan on table t1 it should cost for
parallel degree N, and if that plan then happens to be the cheapest of all the plans to consider
that thats the plan to use.
In this example, the cost of a tablescan at degree two is 94, which is more than the cost of the
indexed access path at 48 which is why Oracle will only take the tablescan if we force the
tablescan with the full hint. But when we indicate a degree of four the cost of the tablescan at
degree four is slightly less than the cost of the serial index path, so Oracle takes it without
needing to be forced into the tablescan.
The parallel hint tells Oracle to change the arithmetic it doesnt force Oracle to run parallel
and it isnt supposed to force Oracle to run parallel.

FEATURE

HINTS

In 10g, we see more and more hints that I call the feature hints. As the Oracle developers
and programmers add a new feature to the optimizers list of tricks they often seem to add a
pair of matching hints to go with the feature. You may have seen the new hash aggregation
that appeared in 10.2 to handle variants of the group by clause. In general its a good idea,
but you could be unlucky and find a few cases where it manages to introduce a performance
problem. So, to go with the new feature, there are two new hints, one to force it to happen, and
one to stop it happening: /*+ use_hash_aggregation */, and /*+ no_use_hash_aggregation */
respectively.
Its worth noting that in 11g theres a new view called v$sql_hint which lists all the hints, the
version they appeared in, and the version where they were firsts coded into stored outlines. If
you see some strange new behavior in a query, its worth checking this view to see if you can
discover any hints that look as if they might be related to the feature.

STRATEGY

HINTS

There are a few hints that allow you to dictate the overall strategy that you want Oracle to take.
I call these the strategy hints, and the small number of hints I put into this list usually come in
pairs:

unnest / no_unnest

push_subq / no_push_subq

merge / no_merge

push_pred / no_push_pred

driving_site
3

Session #239

Database

These are the hints I am happy to use on production systems, as they allow me to impose a
high-level shape on the way Oracle optimises a query without requiring me to micro-manage
every single detail of the query plan. Along with these hints, I also include the qb_name hint
which appeared in 10g to allow you to give names to query blocks.
These hints have the following effects

Unnest /no_unnest: determine whether or not a subquery should operate as a subquery in


the final transformed query (no_unnest), or whether it should be transformed into an inline
view (unnest).

Push_subq / no_push_subq: if you have subqueries that are not going to be unnested, should
they run as the very last steps of the query (no_push_subq the traditional behaviour), or
should they run at the first possible moment (push_subq).

Merge / no_merge: if you use a complex view (e.g. aggregate view, or join view) in your
query, should you rewrite the query to merge the tables in the view into a single from clause
with all the other tables (merge), or should you evaluate the view to produce a standalone
result set and then join the result set to the remaining tables (no_merge).

Push_pred / no_push_pred: If you have a non-mergeable view (possible because of a


no_merge hint) in your query, how should you operate the join from other tables; should you
create one large view result and join it once (no_push_pred) or should you push the join
predicate down into the view definition and recreate the view result set for every driving
row from another table (push_pred).

Driving_site: When running distributed queries, one of the factors affecting performance is
the network traffic. The optimizer does not consider network costs (despite various notes
and documents that suggest otherwise) and will always operate a query from the local
database by default. There are cases where it is more efficient to run the query at a remote
site and then pull the result set back to the local site. The driving_site hint allow use to
specify where the query runs.

In most cases, it is possible to use only these hints to produce a stable and efficient execution
plan; although I will also use the parallel() and parallel_index() hints to force queries into
parallel execution.
But, as I mentioned above, there is one other hint that I use with the strategy hints; the
qb_name() hint that labels query blocks. Consider the following:
select
/*+
qb_name(main)
unnest(@subq2)
no_unnest(@subq4)
no_push_subq(@subq4)
*/
{columns}
from
t1, t3
where
and

t1.n2 = 15
exists (
select
/*+ qb_name(subq2) */
null
from t2
where t2.n1 = 15
4

Session #239

Database

and
and
and

and
t2.id = t1.id
)
t3.n1 = t1.n1
t3.n2 = 15
exists (
select
/*+ qb_name(subq4) */
null
from t4
where t4.n1 = 15
and
t4.id = t3.id
)

Note that I have three query blocks the main query, and two subqueries. I have added a hint
to each query block (and you can use this hint with insert, update, and so on, its not just
queries) to give each query block a name; so I have named the main query block main, the
subquery against table t2 has the name subq2, the subquery against table t4 has the name
subq4.
Once the subqueries are named, I can start using fully-qualified hints which means I specify
the query block that a hint applies to as the first parameter to the hint; so, in the opening set of
hints, I have an unnest() hint, but this applies to the query block called subq2; the
no_unnest() hint applies to query block subq4, and having blocked unnesting for subq4 Ive
also used the no_push_subq() hint referencing subq4 in that hint to tell Oracle not to run it
early.

MICRO-MANAGEMENT
In general the strategy hints (with the parallel and naming hints) are sufficient to solve most
problems of execution plans. However there are a lot of hints that you can use to micro-manage
execution plans. I avoid these wherever possible as it can be very difficult to produce stable
plans if you have to specify every detail.
Consider this fragment of a plan:
...
NESTED LOOP
TABLE ACCESS (FULL) OF T1
TABLE ACCESS BY INDEX ROWID T2
INDEX RANGE SCAN T2_IND_SECOND

-- wrong index

...

While trying to improve the performance of the underlying query, we notice that the optimizer
is choosing the wrong index at this point in the plan, and decide it should be using an index
called t2_ind_first. So we add an index hint to the code:
select /*+ index(t2 t2_ind_first) */

(Note that this is the pre-10g form for an index hint. If youre going to use index hints, you
should get into the habit of specifying them by using the index description (list of columns) in
the hint, not the index name.)
Unfortunately, this works correctly for a while and then one day a change in the data and
statistics makes Oracle choose the following path (which obeys our hint, but doesnt do what
we wanted):
...
HASHJOIN
TABLEACCESS(FULL)OFT1
TABLEACCESSBYINDEXROWIDT2

wrongjoinmethod

Session #239

Database

INDEXFULLSCANT2_IND_FIRST
...

rightindex,wrongaccessmethod.

This is the problem of micro-management you have to do it 100% completely and correctly
and very few people know enough about hints to do this properly.

CONCLUSION
Hints are badly documented, and not well understood. There are some hints that can be used
fairly safely to produce reasonably stable execution plans. There are many hints, though, that
are commonly used and abused without a full appreciation of the potential instability they
introduce to a system.
If you are going to use hints, you want to try to stick with the feature hints and the strategy
hints and avoid the micro-management hints.

Session #239

You might also like