Call (800) 766-1884 for Oracle support & training
Free Oracle Tips


Oracle Consulting Support
Oracle Upgrades
Use New Oracle Features
Oracle Replication Support
Oracle Training
Remote Oracle DBA
System Documentation
Oracle Tips
Oracle Performance
 

Free Oracle Tips


 

HTML Text

BC Oracle tuning

Oracle training

Oracle support

Remote Oracle

 

Oracle Tips by Burleson

Oracle SQL Tuning and CBO Internal

Internals of Oracle Hints

One of the long-running questions about cost-based optimization (CBO) is the meaning of the word hint. As far as CBO is concerned, is a hint a hint, or is it a directive? Personally, I am convinced that the answer is that it is a directive; this chapter argues my case and examines why there is so much room for doubt. This chapter is based on information previously published on the Web at www.jlcomp.demon.co.uk.

My Hint Is Not Working

Hints exist to allow programmers to tell Oracle that they already know the best execution path for a query and give Oracle details of that path so that Oracle will take it. However, there remains a lot of discussion about the word hint, and whether it has its normal English meaning of “a polite but perhaps over-subtle suggestion which may not be noticed and could be ignored” or does it actually mean “a direct command and you had better have a good reason for not doing what you are told”?

 

The most common argument for regarding hints as a “suggestion” that Oracle may ignore comes from the often-heard complaint: “I put this hint in and Oracle still doesn’t do …”. Does this mean that the hint has been ignored? Not necessarily.

 

There are four main reasons why Oracle appears to treat hints with disdain.

n         There are (or have been) bugs in various pieces of Oracle code.

n         Illegal or incorrect hints are not hints; they are comments.

n         Oracle ignores hints that cannot be applied.

n         Most importantly, the optimizer is sufficiently complex that it is possible for Oracle to find ways of making hints inapplicable.

Let’s examine an example of each issue. Consider the hint:

 

/*+index(t1,t1_pk)*/

 

This appears to be a perfectly valid hint that appears (based on names) to be instructing Oracle to use the index representing the primary key when accessing table T1. Due to a bug in various versions of PL/SQL, this hint will not be effective if it appears as part of a SQL statement embedded in a PL/SQL block unless you make sure you have left a space after the plus sign.

 

As an example of an incorrect hint, take a look at the most commonly occurring error in the book:


select

  /*+ index(big1, big1_pk) */

  {list of columns}

from

  big1 t

where

  {list of conditions}

;

 

Again, we appear to be telling Oracle to use an index representing the primary key to access a table big1. The problem here is that we have given the table an alias, and hints have to use the table alias, not the table name. In this case, the hint should be:

 

/*+ index(t, big1_pk) */

 

As an example of a hint that appears valid but cannot be applied, we can highlight another common error. The example is a very simple one that would be very easy to spot, but the nature of the error does cause some surprises in more complex situations.

 

select

  /*+ index(t, big1_uk) */

  count(*)

from

  big1 t

where

  {list of conditions}

  {but none on the Unique Key}

;

 

Note in this example that we have changed to a hint that appears to be asking Oracle to use an index big1_uk that represents a unique constraint to access the table big1.

 

The problem here is that unique constraints do not imply not null constraints (unlike primary key constraints, which imply unique and not null). In this example, and assuming that we had not introduced a not null constraint on at least one of the columns in the unique key, Oracle cannot use the index because there may be rows in the table that do not appear in the index. That is, using the index could produce the wrong answer.

 

Finally, we come to the situations that leave most of us thinking that Oracle is simply ignoring our hints because it wants to. Before looking at, and explaining, an example of Oracle “manufacturing” a reason for rejecting a hint, I would like to offer three logical arguments why hints must really be directives.

n         Oracle Corporation uses hints internally to make code work (we hope) in the most efficient manner. Examine recursive SQL or the SQL generated for parallel query slaves, for example. If hints were simply there to be ignored, entire swathes of the Oracle product base would become unstable.

n         Oracle Corporation has introduced plan stability, also known as stored outlines in Oracle 8.1 to guarantee (so the documents say) that execution paths will not change, even across product upgrades. How is plan stability implemented? As a set of hints stored in the database. So if hints are not directives, plan stability is not stable.

n         If Oracle has the option to ignore hints, how could it decide to ignore them? Presumably, it would have to run through the normal optimization code to find the cost of the path and then decide to take it or not. But one reason for hinting the SQL is to reduce the parse (or, more specifically, optimization) time, so Oracle must be skipping at least some optimization because of the hints. Moreover, if Oracle calculates the cost of your hinted path and then decides whether or not to take it, what criteria does it use in its decision? Does it run the calculations without the hint and compare costs? Presumably not, because that would be just the standard cost-based optimization mechanism.

Clearly, hints make something (not) happen, and there must always be a good reason why Oracle has decided that a hint cannot be applied.

 

It is time for an example. Exhibit 1 is a simple script to build a pair of tables. This script was run on an Oracle 8.1.7 system using a 4K blocksize.

 

-- *************************************************

-- Copyright © 2003 by Rampant TechPress

-- This script is free for non-commercial purposes

-- with no warranties.  Use at your own risk.

--

-- To license this script for a commercial purpose,

-- contact info@rampant.cc

-- *************************************************

 

create table ignore_1

nologging

as

select

  rownum id, rownum val,

  rpad('x',250) padding

from all_objects

where rownum <= 3000;

 

create table ignore_2

nologging

as

select

  rownum id, rownum val,

  rpad('x',250) padding

from all_objects

where rownum <= 500;

 

alter table ignore_2

add constraint ig2_pk primary key (id);

 

analyze table ignore_1

  compute statistics;

analyze table ignore_2

  compute statistics;

Exhibit 1. Creating the Sample Data Set

 

Now we run a simple query against these two tables, joining them on the id column. Our query will pick a small number of rows from the larger table and join them to the matching rows in the smaller table. To encourage Oracle to use the conveniently created primary key index, we will include a hint to tell Oracle to use a nested loop join between the tables (see Exhibit 2).

 

-- *************************************************

-- Copyright © 2003 by Rampant TechPress

-- This script is free for non-commercial purposes

-- with no warranties.  Use at your own risk.

--

-- To license this script for a commercial purpose,

-- contact info@rampant.cc

-- *************************************************

 

set autotrace traceonly explain

 

select /*+ use_nl(i2) */

       i1.val val1,

       i2.val val2

from

       ignore_1 i1,

       ignore_2 i2

where

       i2.id = i1.id

and    i1.val <= 10

;

 

SELECT STATEMENT Optimizer=ALL_ROWS

  HASH JOIN  (Cost=44 Card=11 Bytes=132)

    TABLE ACCESS (FULL) OF 'IGNORE_1' ( (Cost=36 Card=11)

    TABLE ACCESS (FULL) OF 'IGNORE_2' ( (Cost=7 Card=500)

Exhibit 2. A Query that Will Not Take the Hint

 

Our query, with the hint, visits the tables in the order we expect but appears to ignore our hint to use_nl(i2). The definition of this hint means:

 

When examining paths entering table (aliased by) i2, you should consider only a nested loop join to get into table i2.

 

At this point, some developers will change the hint to read:

 

/*+ use_nl(i1 i2) */

 

and find that the query suddenly changes to the nested loop that they expect. This is sometimes taken as an indication that you need to specify both ends of the nested loop join in this hint. This is not true; the altered path would actually be a side effect of, not a direct result of, this change. In its modified form, the hint now reads:

 

When examining paths entering the table (aliased by) i2, you should consider only a nested loop join to get into table i2; and when examining paths entering the table (aliased by) i1, you should consider only a nested loop join to get into table i1.

 

Other developers may come up with the “more correct” solution of changing the hint to read:

 

/*+ ordered use_nl(i2) */

 

This tells Oracle to visit the tables i1 and i2 in the correct order (as listed in the from clause) and to use a nested loop to get into i2.

 

Why is there a problem, and why is this the solution?

 

The answer comes from examining the 10053 trace file. If we have no hints, the trace file will show the stages (edited for extreme compaction) shown in Exhibit 3. The interesting quirk appears when we use only the use_nl(i2) hint. We have not told Oracle to avoid examining join orders from i2 to i1, so the trace looks like Exhibit 4.


Try the order i1 to i2 using NL.

Try the order i1 to i2 using sort/merge

Try the order i1 to i2 using hash

  Hash is the cheapest

 

Try the order i2 to i1 using NL.

Try the order i2 to i1 using sort/merge

Try the order i2 to i1 using hash

  Hash is the cheapest

 

Hash i1 -> i2 is the cheapest overall

Exhibit 3. A Shortened 10053 Trace

 

Thus, given the restrictions imposed by the hints, the best available execution path from Oracle’s perspective is to hash the two tables going in the order i2 to i1 — but swapping the join inputs. That is, Oracle has legally found a way of bypassing our demand that it should only consider a nested loop when entering table i2. As we can see from Exhibit 4, when deliberately considering methods of getting into i2, the only option examined was the nested loop. The unexpected hash join appears only as an “accidental” by-product of considering paths into i1. This, of course, is why the addition of the ordered hint is the correct solution to the problem; it stops Oracle from considering any paths into i1 and therefore blocks the emergence of the swapped hash join.

 

Try the order i1 to i2 using NL.

  Use_nl is the cheapest

 

Try the order i2 to i1 using NL.

Try the order i2 to i1 using sort/merge

Try the order i2 to i1 using hash,

Try the hash again with "(sides swapped)"

  Swapped Hash is the cheapest

 

Hash i2 -> i1 is the cheapest overall

Exhibit 4. The Trace after the Use_Nl() Hint

 

Just for the sake of completeness, Exhibit 5 shows an extract of the trace information you would get from the two hint modifications given above.

 

/*+ ordered use_nl(i2) */

 

Try the order i1 to i2 using NL.

  Nested loop is the cheapest

 

NL i1 -> i2 is the cheapest overall

 

/*+ use_nl(i1,i2) */

 

Try the order i1 to i2 using NL.

  Nested loop is the cheapest

 

Try the order i2 to i1 using NL.

  Nested loop is the cheapest

 

NL i1 -> i2 is the cheapest overall

Exhibit 5. Alternative Traces

Conclusion

Hints are not mildly worded suggestions; they are directives to the optimizer to reduce the number of different options it is allowed to examine.

 

Unfortunately, it is easy to be convinced for several reasons that your hints are being ignored; and this may simply mean that there is something trivially wrong with your hinting.

 

There are cases, however, where the increasing sophistication of the optimizer allows it to use alternative mechanisms to reach a path that you thought you had blocked by the use of hints.

 

If you are going to use hints, be very thorough with the hints you apply. If there is a valid path that you want Oracle to take, the correct and complete set of hints will ensure that Oracle does take it.

 

 



Download your Oracle scripts now:

www.oracle-script.com

The definitive Oracle Script collection for every Oracle professional DBA

  
 

Oracle performance tuning software 
 
 
 
 

Oracle performance tuning book

 

 
 
 
Oracle performance Tuning 10g reference poster
 
 
 
Oracle training in Linux commands
 
Oracle training Excel
 
Oracle training & performance tuning books
 
   

Copyright © 1996 -  2014 by Burleson. All rights reserved.

Oracle® is the registered trademark of Oracle Corporation.