Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Nag.

Asked: November 12, 2001 - 4:46 pm UTC

Last updated: July 19, 2011 - 1:17 pm UTC

Version: 8.1.7

Viewed 10K+ times! This question is

You Asked

Tom

We are all under the understanding that our production database is USING THE RBO.

But, recently I happened to observe that some of the queries (joining 5 to 7 tables) are using the FULL hint.

Iam perplexed, because from the oracle sql tuning manual and from your discussions

"The optimizer recognizes hints only when using the cost-based approach. If you include a hint (except the RULE hint) in a statement block, then the optimizer automatically uses the cost-based approach."

I got back to our DBA, and let him know the same , but they argue bluntly that the optimizer is not CBO , as they are not analyzing the tables.( the optimizer_mode is set to CHOOSE).

I need a convincing example to prove the contrary.

You assistance is needed very dearly. Iam very confident that the best execution path is not being chosen, as the tables are not analyzed and teh cbo is being used due to the use of the FULL hint.

Thank you



and Tom said...

If there is a WELL FORMED HINT in the query -- that query IS using the cost based optimizer -- UNLESS the hint is in fact a RULE hint (can you point me to where I've said the above? Its not accurate).

The parser recognizes hints and will use the cost based optimizer. If you include a hint (except a RULE hint) in a SQL statement, then the optimizer will be the CBO.


The example is trivial:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table emp as select * from scott.emp;
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table dept as select * from scott.dept;
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index emp_idx on emp(deptno);
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp, dept where emp.deptno = dept.deptno;

Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 NESTED LOOPS
2 1 TABLE ACCESS (FULL) OF 'DEPT'
3 1 TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
4 3 INDEX (RANGE SCAN) OF 'EMP_IDX' (NON-UNIQUE)

Ok, this is RBO above -- we can TELL that since there is no "Card", "Cost" information in the plan...

ops$tkyte@ORA817DEV.US.ORACLE.COM> select /*+ full */ * from emp, dept where emp.deptno = dept.deptno;

Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 NESTED LOOPS
2 1 TABLE ACCESS (FULL) OF 'DEPT'
3 1 TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
4 3 INDEX (RANGE SCAN) OF 'EMP_IDX' (NON-UNIQUE)

There we used an improperly formed hint (full MUST be followed by () in order to be well formed). We are still using RBO...

ops$tkyte@ORA817DEV.US.ORACLE.COM> select /*+ full(emp) */ * from emp, dept where emp.deptno = dept.deptno;

Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=67 Bytes=7839)
1 0 MERGE JOIN (Cost=6 Card=67 Bytes=7839)
2 1 SORT (JOIN) (Cost=4 Card=82 Bytes=7134)
3 2 TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=82 Bytes=7134)
4 1 SORT (JOIN) (Cost=3 Card=82 Bytes=2460)
5 4 TABLE ACCESS (FULL) OF 'DEPT' (Cost=1 Card=82 Bytes=2460)

Now, we can see we are using CBO with a well formed hint, we see the cost and card -- in fact, the plan is different, something that would not be possible with the RBO

Going further:


1* select /*+ full(a) */ * from emp, dept where emp.deptno = dept.deptno
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=67 Bytes=7839)
1 0 MERGE JOIN (Cost=6 Card=67 Bytes=7839)
2 1 SORT (JOIN) (Cost=4 Card=82 Bytes=7134)
3 2 TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=82 Bytes=7134)
4 1 SORT (JOIN) (Cost=3 Card=82 Bytes=2460)
5 4 TABLE ACCESS (FULL) OF 'DEPT' (Cost=1 Card=82 Bytes=2460)


we used a well formed but INVALID hint (no table A in there). We still use the CBO

So the quick litmus test is to use autotracae in plus -- it'll show you right off what optimizer you are using.

I should say -- that with 817, you are missing out on alot by NOT using the CBO.

Rating

  (27 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Helena Markova, November 13, 2001 - 3:27 am UTC


CBO

Andre Whittick Nasser, November 13, 2001 - 7:16 am UTC

Some questions:

1) When you use a correct hint, like FULL(emp), then you are forcing emp to undergo a FTS, and, in undergoing a FTS, the join behavior will adapt to what the hint says, for example, doing a sort-merge join. Right ?

2) Even if you have not analyzed any of the tables ? Where does the CBO have the statistics from ? That reminds me of FIRST_ and ALL_ROWS. Even with no stats, the CBO is able to take assumptions based on "heuritics" (nice word, but what does it mean in Oracle) ?

3) To the best of my knowledge, I have never used RBO in production. Does it ignore hints like FULL, etc ? When Oracle says RBO is "syntax-driven" are not hints part of this "syntax", or only things like the "driving table" in the FROM table order are considered, not hints ?

4) I know "cost" metrics are not very meaningful. But what sense can I make of it ? What about "card" and "bytes" ? How useful can they be, REALLY ?

Thanks once more, from (on this morning) rainy Rio de Janeiro !

Tom Kyte
November 13, 2001 - 8:19 am UTC

1) yes -- IF it accepts the hint, hints are just that -- hints. They are NOT directives, they are suggestions. It took the suggestion in this case.

2) Correct -- CBO makes up generalized statistics if none exist. Heuristics mean "rules" basically -- the CBO has a set of rules it applies to derive stats for objects that have none.

<quote design/tuning for performance guide>
If the optimizer uses the cost-based approach for a SQL statement, and if some
tables accessed by the statement have no statistics, then the optimizer uses internal information (such as the number of data blocks allocated to these tables) to estimate other statistics for these tables.
</quote>


3) if you have HINTS -- you are NOT using RBO (I thought that would be clear from the example!! thats what this entire example shows -- drop a well formed hint in there and the RBO goes out the window -- unless the well formed hint is /*+ RULE */).

If you have HINTS, you are using CBO and the syntax of the query (order of tables, etc) is not relevant.

4) Card is cardinality -- an estimate of the number of rows that each step will work on/produce. Bytes is the estimated number of bytes each step will produce. The cost can be useful to decide why a certain plan was choosen (see Jonathan Lewis's Practical Oracle8i book -- he has a good discussion of this, excellent book all around).

To wrap it up !

Andre Whittick Nasser, November 13, 2001 - 12:17 pm UTC

So,

In sum:

ANY HINT - /*+ RULE */ => CBO

Correct ? (yes/no/why)

Tom Kyte
November 13, 2001 - 12:51 pm UTC

Correct, yes. Why -- becauses thats the way the server is programmed -- you want to use a hint -- only the CBO understands hints, hence the CBO is used

Why a query performs better only when /*+ rule */ hint is used?

A Reader, November 07, 2002 - 2:49 pm UTC

Hi Tom,
I know we should use CBO but in some queries we have, we have to user a /*+rule*/ hint to make them perform better. The optimizer_mode=choose. We have updated statistics for all the tables. We have to use the hint not only because it forces the queries to use the existing indexes, but also because it really returns results noticeably faster. We tried to rewite in different ways but none of them as good as using the rule hint. Are there any side effects on using the "rule" hints? Should we continue trying utill we find the matching CBO?
Thank you for your time.

Tom Kyte
November 07, 2002 - 3:30 pm UTC

I would prefer to use the optimizer_index_cost_adj init.ora (or session) parameter. I like the number "35". Give that a try.

Optimizer_Goad=First_Rows

Mike, November 07, 2002 - 4:52 pm UTC

I read somewhere that using the FIRST_ROWS optimizer goal or hint would tend to use indexes more than the ALL_ROWS goal, so it would be more like using the RULE based optimizer. Is that true?

Tom Kyte
November 07, 2002 - 7:17 pm UTC

I would prefer to use the cost adj -- but yes, first rows makes the CBO "index happy" or "index crazy"

Dave, November 07, 2002 - 5:47 pm UTC

Can you describe the way in which optimizer_index_cost_adj and optimizer_index_caching relate? It seems to me that if you promote the use of indexes through lowering the former parameter, then the latter would also need adjustment to reflect the true state of the cache. I wonder whether you could get stuck in a kind of feedback loop where promoting the use of indexes leads to greater likelihood of index usage, hence more likelihood of finding the indexes in cache, hence there is a lower cost associated with using them, hence you promote the use of them more ... until you end up with optimizer_index_caching=100 and optimizer_index_cost_adj=1!

Also, I have a view defined as ...

create or replace view advise_index_cost_adj
as
select 'Actual is '
||LTrim(to_char(seq_read_wait/scattered_wait*100,'9990.0'))
||', system is set to '||init_value message
from (
select average_wait seq_read_wait
from v$system_event where event ='db file sequential read'),
(
select average_wait scattered_wait
from v$system_event where event ='db file scattered read'),
(
select value init_value from v$parameter
where name = 'optimizer_index_cost_adj');

... that gives me output such as ...

"Actual is 7.1, system is set to 12"

.. for a quick snapshot of index cost adjustment.

Is the advice that it's giving (OK, that I'm giving myself) reliable, or am I fooling myself here?

Tom Kyte
November 07, 2002 - 7:29 pm UTC

Not at all - you don't need to touch caching to use cost adj. In fact, i've personally never used the caching one, only the cost adj one.

Never saw a "feedback" loop like that -- no.

If you have Jonathan Lewis's book "practical Oracle8i building efficient databases" -- he has a discussion on this. He sums it up somewhat by saying:

"in effect the parameter COST ADJ behaves as if it were dictating to the optimizer the degree to which is can expect the TABLE to be cached, whereas the parameter index caching tells the optimizer the degree to which it can expect the index to be cached"

cost adjust is a more "aggressive" caching one.

(if you don't have his book -- you should get it, it's really good).


I'm not quite sure where you are going with that ration -- the one of sequential (single block typically) IO wait to scattered (multi-block typically) IO wait?

Dave, November 08, 2002 - 8:30 am UTC

Quote: I'm not quite sure where you are going with that ration -- the one of sequential
(single block typically) IO wait to scattered (multi-block typically) IO wait?

Uh-oh.

Wellllllllll, supposing that sequential read waits are mostly associated with indexes and (maybe?) access by rowid on a table, and scattered read waits are mostly associated with tables (excepting perhaps index scans, "fast full" or otherwise) then we might get an estimate of one element of the overhead associated with reading tables via full scan versus reading them via index access.

Or maybe not. This could be shaky ground.

Tom Kyte
November 08, 2002 - 9:18 am UTC

I think "shaky".

Suppose I had one billion waits for full scans -- looks like alot of time.
I have one thousand for index reads -- looks small.

Your ratio would make full scans look evil however -- if we flipped it an started using indexes it would be really bad (it could be, it might not be, it all depends). It could be that we avoid a billion waits on the full scans but introduce 5 billion waits on seq IO.

We would have to compare the cost of doing the scattered IO for a working set of queries VS the cost of doing seq IO for the same set -- something you cannot do really unless you have both sets of numbers!

FIRST_ROWS(n) HINT

pasko, November 08, 2002 - 10:04 am UTC

Hi Tom,
whats the major difference performance-wise , between the hints FIRST_ROWS and the new one in 9iR2 : FIRST_ROWS(n) ?

I have tried :

select /*+ FIRST_ROWS(20) */
count(*)
from emp
where rownum <= 100000 ;

but i still see the hint being used and also the STOP KEY in the Query plan..

I didn't see any performance improvements after i switched FIRST_ROWS with FIRST_ROWS(n) .

How best can we use this new HINT ?

Best regards





Tom Kyte
November 08, 2002 - 10:48 am UTC

it'll only affect things in a very fine grained manner.

Given your query -- there really is only TWO or THREE approaches to take regardless of how the query is optimized:

o full scan emp for 100,000 rows
o index range scan on emp_idx if emp_idx is an index on a not-null column
o index fast full scan on emp_idx if emp_idx is an index on a not-null column

regardless of the optimization technique -- the plan will most likely stay exactly the same.

Please help me to solve these puzzle

Bharath, November 08, 2002 - 10:22 am UTC

>>If there is a WELL FORMED HINT in the query -- that query >>IS using the cost based optimizer

Tom please help me from the puzzle

Question 1,we are using 8.1.7.4 (Optimizer = rule Both in Prod & Dev ) it means that we cant give a hint in the query ,the reason why i am asking is in your response rule based optimizer doesnt understand the hint

Question2)If i give the well formed Hint in the query will the oracle optimizer will switch to CBO even if the oracle optimizer mode is set to rule

Question3)Why Rule based optimizer dosent understand the hint.



Tom Kyte
November 08, 2002 - 10:58 am UTC

question 1) your premise is FALSE.  You can have RBO set as the default but the second you pop a valid hint in there -- you are using CBO. Consider (autotrace shows which optimizer we are using -- if is has COST/CARD, you are using CBO, else you are using RBO)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table emp as select * from scott.emp;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table emp compute statistics;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set optimizer_goal=choose;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=14 Bytes=560)
   1    0   TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=14 Bytes=560)

<b>table is analyzed, in choose that implies CBO...</b>


ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set optimizer_goal=rule;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   TABLE ACCESS (FULL) OF 'EMP'

<b>even though stats are present -- we can see we are using RBO here BUT...</b>

ops$tkyte@ORA817DEV.US.ORACLE.COM> select /*+ FULL(EMP) */ * from emp;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE (Cost=1 Card=14 Bytes=560)
   1    0   TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=14 Bytes=560)

<b>you can see the hint puts us BACK into CBO!!!</b>

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off


question 2) yes.

question 3) because HINTS are only valid with CBO.  RBO = RULE based optimization.  It uses RULES to come up with a query plan.  CBO doesn't use a hard, fast list of rules to come up with the plan.  It takes tons of variables into consideration -- including the hints. 

Dave, November 08, 2002 - 12:26 pm UTC

Quote: Suppose I had one billion waits for full scans -- looks like a lot of time.  I have one thousand for index reads -- looks small.

What if we start by looking not at the quantity of reads, but at the average wait time instead? From that perspective the view might be suggesting whether it would be a worthwhile to gain 1,000,000,000 sequential read waits while reducing scattered read waits by only 500,000,000 -- total db_file wait time would be reduced even though the total db_file number of waits has increased.


  1  select TOTAL_WAITS,TIME_WAITED,AVERAGE_WAIT
  2*        from v$system_event where event ='db file scattered read'
SQL> /

TOTAL_WAITS TIME_WAITED AVERAGE_WAIT
----------- ----------- ------------
      19543      284113   14.5378396

SQL> select TOTAL_WAITS,TIME_WAITED,AVERAGE_WAIT
  2         from v$system_event where event ='db file sequential read'
  3  /

TOTAL_WAITS TIME_WAITED AVERAGE_WAIT
----------- ----------- ------------
     192772      246160   1.27694893

Given the ratio of (avg sequential read wait time)/(avg scattered read wait time) of 1:11 that I see on my system, I might be willing to take on many more sequential reads for each scattered read that I eliminate. All completely theoretical of course, and it probably represents about 1/100th of the total system performance from the user point of view.

Note that:

i) this is a small data warehouse db, heavy on bitmap indexes and range partitions, if that makes a difference
ii) I don't have a performance problem, I'm just tuning for the sake of it ;)

Hey, it could benefit someone else though, right? I'm sure that index_optimizer_cost_adj is a much neglected parameter -- when I took over this database an (obvious) adjustment from "100" down to "15" reduced average report time from 75 seconds down to 15 seconds. And speaking of the users point of view, not a single one of them noticed this (literally) overnight improvement -- an example which you are quite free to mention when bashing people for their never-ending tuning exercises.

Talking of which, I'm now off to implement composite partitioning, bitmap join indexing, and data segment compression on a major fact table in order to get that average below ten seconds. Like anyone cares. 

Final Answer

Bharath, November 08, 2002 - 1:43 pm UTC

What is Difference Between Well formed and Not well formed hints?If we place Not well formed hint then it means that parser will simply reject it.

Tom Kyte
November 08, 2002 - 2:14 pm UTC

select /*+ Hello World */ * from dual;

is not well formed -- it is not a valid hint.


select /*+ full */ * from dual;

is not well formed -- FULL needs to be in the format FULL(tablename) in order to be well formed.


select /*+ full(dual) */ * from dual

is well formed. It is not that it rejects it -- it ignores hints that are not valid hints -- as if they were not in the text at all.

A reader, March 17, 2003 - 2:50 pm UTC

Hi Tom, 

We are using siebel CRM. siebel recommands rule base optimizer. we have other ssystem in Oracle that use cost based Optimizer.

While doing query we have to join tables from both Siebel crm(Rule base opt.) and other systems(cost based optimizer).

--I have seen lots of performance problem 
What is your suggestions on this matter?

2) Star hint

We have some bad queries that is taking more then five(5) minitues, if I use /*+ star */ hint it run very fast and return result with in 10 or less SECONDS !!!!

Could you explain why this is happening  ? 
We have mix enviournment (Both rule based and cost bas)

For eg. 

---------------------
Without Hint
---------------------

  1  select   'OPTY', op.row_id, sysdate, 'FOR_ASSIGNMENT', 'ASSIGNMENT CHANGE - HOT PROS OPTY - ZIP
  2   from siebel.s_opty op, siebel.s_contact cn, siebel.s_addr_per ad,
  3    sblint.release_rep_assignment rra, siebel.s_stg stg, mkt.listing_state@oraprod1@mkt_link st
  4   where op.pr_con_id = cn.row_id and cn.pr_per_addr_id = ad.row_id
  5    and op.opty_cd in ('Hot Prospect', 'Prospect')
  6    and ( op.x_sub_type <> 'Last Year Cancel' or op.x_sub_type is null )
  7              and ( nvl(op.x_source,' ') <> 'Last Year Cancel')                    -- dz 2/2003
  8    and rra.sales_level_code = 3 and stg.stage_status_cd = 'Open'
  9    and op.curr_stg_id = stg.row_id
 10    and substr(ad.zipcode, 0, 5) = rra.zip_code
 11    and rra.zip_code is not null
 12        --    add code to match on state, county, and city  - zeller
 13         and ad.city = rra.city and ad.county = rra.county and ad.state = st.state
 14    and st.state_no = rra.listing_state_no
 15    and not exists (select * from sbl_assignment where object_id = op.row_id and object = 'OPTY'
 16*       and status <> 'Archived')
SQL> /

no rows selected


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   FILTER
   2    1     NESTED LOOPS
   3    2       NESTED LOOPS
   4    3         NESTED LOOPS
   5    4           NESTED LOOPS
   6    5             NESTED LOOPS
   7    6               REMOTE*                                        ORAPROD1
                                                                       .WORLD@M
                                                                       KT_LINK

   8    6               TABLE ACCESS (BY INDEX ROWID) OF 'S_ADDR_PER'
   9    8                 INDEX (RANGE SCAN) OF 'S_ADDR_PER_M2' (NON-U
          NIQUE)

  10    5             TABLE ACCESS (BY INDEX ROWID) OF 'RELEASE_REP_AS
          SIGNMENT'

  11   10               INDEX (RANGE SCAN) OF 'RELEASE_REP_IDX2' (NON-
          UNIQUE)

  12    4           TABLE ACCESS (BY INDEX ROWID) OF 'S_CONTACT'
  13   12             INDEX (RANGE SCAN) OF 'S_CONTACT_PR_PER_ADDR_ID_
          X' (NON-UNIQUE)

  14    3         TABLE ACCESS (BY INDEX ROWID) OF 'S_OPTY'
  15   14           INDEX (RANGE SCAN) OF 'S_OPTY_M9' (NON-UNIQUE)
  16    2       TABLE ACCESS (BY INDEX ROWID) OF 'S_STG'
  17   16         INDEX (UNIQUE SCAN) OF 'S_STG_P1' (UNIQUE)
  18    1     TABLE ACCESS (BY INDEX ROWID) OF 'SBL_ASSIGNMENT'
  19   18       INDEX (RANGE SCAN) OF 'SBL_ASSIGNMENT_IDX1' (NON-UNIQU
          E)



   7 SERIAL_FROM_REMOTE            SELECT "STATE","STATE_NO" FROM "MKT"."LISTIN
                                   G_STATE" "ST"



Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
   49228973  consistent gets
     300521  physical reads
         68  redo size
        280  bytes sent via SQL*Net to client
        249  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

---------------------
With  /*+ star */ Hint
---------------------


SQL> ed
Wrote file afiedt.buf

  1  select  /*+ star */  'OPTY', op.row_id, sysdate, 'FOR_ASSIGNMENT', 'ASSIGNMENT CHANGE - HOT PRO
  2   from siebel.s_opty op, siebel.s_contact cn, siebel.s_addr_per ad,
  3    sblint.release_rep_assignment rra, siebel.s_stg stg, mkt.listing_state@oraprod1@mkt_link st
  4   where op.pr_con_id = cn.row_id and cn.pr_per_addr_id = ad.row_id
  5    and op.opty_cd in ('Hot Prospect', 'Prospect')
  6    and ( op.x_sub_type <> 'Last Year Cancel' or op.x_sub_type is null )
  7              and ( nvl(op.x_source,' ') <> 'Last Year Cancel')                    -- dz 2/2003
  8    and rra.sales_level_code = 3 and stg.stage_status_cd = 'Open'
  9    and op.curr_stg_id = stg.row_id
 10    and substr(ad.zipcode, 0, 5) = rra.zip_code
 11    and rra.zip_code is not null
 12        --    add code to match on state, county, and city  - zeller
 13         and ad.city = rra.city and ad.county = rra.county and ad.state = st.state
 14    and st.state_no = rra.listing_state_no
 15    and not exists (select * from sbl_assignment where object_id = op.row_id and object = 'OPTY'
 16*       and status <> 'Archived')
SQL> /

no rows selected


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE (Cost=886 Card=1 Bytes=318)
   1    0   FILTER
   2    1     NESTED LOOPS (Cost=886 Card=1 Bytes=318)
   3    2       NESTED LOOPS (Cost=60 Card=1 Bytes=300)
   4    3         NESTED LOOPS (Cost=58 Card=1 Bytes=213)
   5    4           MERGE JOIN (CARTESIAN) (Cost=57 Card=1 Bytes=196)
   6    5             NESTED LOOPS (Cost=41 Card=1 Bytes=124)
   7    6               TABLE ACCESS (FULL) OF 'S_STG' (Cost=1 Card=20
           Bytes=520)

   8    6               TABLE ACCESS (BY INDEX ROWID) OF 'S_OPTY' (Cos
          t=2 Card=1 Bytes=98)

   9    8                 INDEX (RANGE SCAN) OF 'S_OPTY_F5' (NON-UNIQU
          E) (Cost=1 Card=1)

  10    5             SORT (JOIN) (Cost=55 Card=1 Bytes=72)
  11   10               TABLE ACCESS (FULL) OF 'RELEASE_REP_ASSIGNMENT
          ' (Cost=16 Card=1 Bytes=72)

  12    4           REMOTE* (Cost=1 Card=74 Bytes=1258)                ORAPROD1
                                                                       .WORLD@M
                                                                       KT_LINK

  13    3         TABLE ACCESS (BY INDEX ROWID) OF 'S_ADDR_PER' (Cost=
          2 Card=2000 Bytes=174000)

  14   13           INDEX (RANGE SCAN) OF 'S_ADDR_PER_M1' (NON-UNIQUE)
           (Cost=1 Card=2000)

  15    2       TABLE ACCESS (BY INDEX ROWID) OF 'S_CONTACT' (Cost=826
           Card=2000 Bytes=36000)

  16   15         INDEX (FULL SCAN) OF 'S_CONTACT_F6' (NON-UNIQUE) (Co
          st=26 Card=2000)

  17    1     TABLE ACCESS (BY INDEX ROWID) OF 'SBL_ASSIGNMENT' (Cost=
          2 Card=1 Bytes=63)

  18   17       INDEX (RANGE SCAN) OF 'SBL_ASSIGNMENT_IDX1' (NON-UNIQU
          E) (Cost=1 Card=1)



  12 SERIAL_FROM_REMOTE            SELECT "STATE","STATE_NO" FROM "MKT"."LISTIN
                                   G_STATE" "ST" WHERE "STATE_NO"=:1



Statistics
----------------------------------------------------------
          0  recursive calls
          8  db block gets
      25797  consistent gets
       6508  physical reads
          0  redo size
        280  bytes sent via SQL*Net to client
        249  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
         32  sorts (memory)
          0  sorts (disk)
          0  rows processed

SQL> 
SQL> 

--LOTS of  difference between statistics!!!

Thanks,

 

Tom Kyte
March 17, 2003 - 2:59 pm UTC

1) simple -- get rid of sighbel ;)

seriously -- on queries that involve tables with stats and without stats -- you are ASKING for big time trouble. You'll either be hinting the query literally to death (doing the work of the cbo) or you'll be using the RBO (and missing out on all of the really cool things the cbo can do which the RBO hasn't a chance). also, if you use objects such as IOT's or partitioned tables - you have no choice, you'll be using the CBO and most likely using hints.


2) simple -- the RBO is very stupid. It has a tiny set of rules (documented in the tuning guide) by which is processes any query. It doesn't use any statistics, it doesn't do anything smart. It just applies the rules and away it goes.

Look at this one:
</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:6749454952894 <code>





rbo switches to cbo with well formed hint

Sujata, April 03, 2003 - 10:06 am UTC

For Question: (By Bharath)
"Question2)If i give the well formed Hint in the query will the oracle optimizer
switch to CBO even if the oracle optimizer mode is set to rule"

Your answer was yes...

My Question:
1.Will it switch to CBO regardless of whether the table
is analyzed or not at this point?
2.If it does switch regardless..and the table is not analyzed....no statistics..then you mentioned..that
CBO will guess the statistics based on blocks etc...
So, will it better to have those tables that are using hints analyzed...


Tom Kyte
April 03, 2003 - 10:50 am UTC

1) yes.

2) but -- if you are using hints, you are basically doing the optimizers job, the optimizer doesn't need stats. You'll sit there in sqlplus hinting the query to do exactly what you want. statistics won't be something you want really. You'll take a query:

select * from t where upper(ename) = :x

and you'll hint it to be

select /*+ index( t my_fbi ) */ * from t where upper(ename) = :x

it won't need stats cause you already told it what to do.

Why is it not using the FBI?

Sachin, May 19, 2003 - 2:28 pm UTC

8.1.7.4.0 (running RBO)
O.K. We have a table that has PROJID as PK and following indexes on it:
NON-UNIQUE INDEX I_PROJ_PROJECT_PREVPROJID (PREVIOUS_PROJID)
UNIQUE INDEX I_PROJ_PROJECT_PROJNAME (PROJNAME, PROJID)
UNIQUE INDEX I_PROJ_PROJECT_UPPER_PROJID (UPPER(PROJID))
NON-UNIQUE INDEX PROJ_PROJECT_UPPER_PROJNAME (UPPER(PROJNAME))

I tried the following query:
select COUNT(*) FROM PROJ_PROJECT
WHERE UPPER(PROJID) LIKE '97%';

It returned
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'PROJ_PROJECT'




Statistics
----------------------------------------------------------
0 recursive calls
14 db block gets
13326 consistent gets
10484 physical reads
0 redo size
207 bytes sent via SQL*Net to client
253 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

Then I did this:
select /*+ INDEX(I_PROJ_PROJECT_UPPER_PROJID) */ COUNT(*) FROM PROJ_PROJECT
WHERE UPPER(PROJID) LIKE '97%';

and it returned:
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE (Cost=4 Card=1 Bytes=15)
1 0 SORT (AGGREGATE)
2 1 INDEX (FAST FULL SCAN) OF 'I_PROJ_PROJECT_PROJNAME' (UNI
QUE) (Cost=4 Card=20017 Bytes=300255)





Statistics
----------------------------------------------------------
0 recursive calls
10 db block gets
4361 consistent gets
1532 physical reads
0 redo size
206 bytes sent via SQL*Net to client
253 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

There were 41490 records involved.

Question:
Why did it ignore the index on UPPER(PROJID)?
This same behaviour was also there while trying to hint for
upper(projname) index.

Any idea about the above...

Sachin, May 22, 2003 - 11:26 am UTC


Tom Kyte
May 23, 2003 - 8:20 am UTC

8.1.7.4.0 (running RBO) <<<<=== says it all.

function based indexes (and hundreds of other goodies) use the CBO only.


"neutral" hint

Frank, May 13, 2004 - 1:49 am UTC

Hi,

Our batches run in RBO, whereas our on-line applications runs in CBO-mode. (doesn't sound too great, I know).
Our customers have a choice whether their optimizer_mode is FIRST_ROWS or FIRST_ROWS_(n).
I tuned this statement to perform in CBO; whereas in RBO it uses a path I don't like.
Is there some sort of neutral hint that will only force CBO, without really influencing the CBO ? I would like it to work in the customer's optimizer_mode.
I have thought about the CHOOSE-hint, but Performance & tuning guide says about choose-hint: "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"
I take that it will use ALL_ROWS optimizer mode ?

(DB version is 9.2.0.5)

Thanks,
Frank

Tom Kyte
May 13, 2004 - 10:18 am UTC

the cbo needs to know whether to optimizer for response time (first rows) or throughput (all rows). you pick -- that is "as neutral" as it gets. choose is very much "all rows" like.

RBO is not index happy

AR, August 27, 2004 - 3:54 pm UTC

Tom,
I just inherited an application that runs on 7.3.4. Yes, itÂ’s really old :). But IÂ’m at a loss here and could really use your help!

HereÂ’s whats happening. 
1) The database is set to use RBO. 
2) Table AR.S_ASSET has never been analyzed. I infact deleted statistics from it to make sure.
3) Column PROD_ID is the leading column in unique concatenated index S_ASSET_U1_TEMP
4) select count(ASSET_NUM) from ar.s_asset; resorts to a FTS everytime! Even if I set the optimizer_goal=CHOOSE at the session level. Why??
5) select /*+ index(S_ASSET S_ASSET_U1_TEMP) */ count(PROD_ID) from ar.s_asset; uses the index (and thus resorts to CBO due to hint) and works quite nicely as expected.

Could you explain step 4 and what I could do to make this “index happy”? This is just the tip of the iceberg. The whole application I am told is performing poorly. The application requires an RBO apparently. 

Here are details of the above steps if it helps. I have described the table and index details at the bottom. It is wide. 
PS : I also did try analyzing the table to see if makes a difference to this exercise, and it didnÂ’t. It yielded similar results.

1)
SVRMGR> show parameter opt
NAME TYPE VALUE
----------------------------------- ------- ------------------------------
optimizer_mode string RULE
optimizer_parallel_pass boolean TRUE
optimizer_percent_parallel integer 0
optimizer_search_limit integer 5

2) 
SQL> analyze table ar.s_asset delete statistics;
Table analyzed.

3) 
SQL> select INDEX_NAME,COLUMN_NAME,COLUMN_POSITION from user_ind_columns where table_name='S_ASSET';

INDEX_NAME                     COLUMN_NAME                    COLUMN_POSITION
------------------------------ ------------------------------ ---------------
S_ASSET_U1_TEMP                ASSET_NUM                                    1
S_ASSET_U1_TEMP                PROD_ID                                      2
S_ASSET_U1_TEMP                CONFLICT_ID                                  3

4) 
SQL> l
  1* select count(ASSET_NUM) from ar.s_asset
SQL> /
COUNT(ASSET_NUM)
----------------
          292216
Elapsed: 00:00:06.14
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'S_ASSET'

SQL> alter session set optimizer_goal=CHOOSE;
Session altered.

SQL> select count(ASSET_NUM) from ar.s_asset;
COUNT(ASSET_NUM)
----------------
          292216
Elapsed: 00:00:06.16
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'S_ASSET'
Statistics
----------------------------------------------------------
          0  recursive calls
          2  db block gets
      11209  consistent gets
      11209  physical reads
          0  redo size
        195  bytes sent via SQL*Net to client
        280  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

5) 
SQL> l
  1* select /*+ index(S_ASSET S_ASSET_U1_TEMP) */ count(ASSET_NUM) from ar.s_asset
SQL> /
COUNT(ASSET_NUM)
----------------
          292216
Elapsed: 00:00:03.46
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=26 Card=456431 Bytes
          =23734412)

   1    0   SORT (AGGREGATE)
   2    1     INDEX (FULL SCAN) OF 'S_ASSET_U1_TEMP' (UNIQUE) (Cost=26
           Card=456431 Bytes=23734412)
Statistics
----------------------------------------------------------
          8  recursive calls
          0  db block gets
       2493  consistent gets
          0  physical reads
          0  redo size
        195  bytes sent via SQL*Net to client
        318  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> desc s_asset
 Name                            Null?    Type
 ------------------------------- -------- ----
 ROW_ID                          NOT NULL VARCHAR2(15)
 CREATED                         NOT NULL DATE
 CREATED_BY                      NOT NULL VARCHAR2(15)
 LAST_UPD                        NOT NULL DATE
 LAST_UPD_BY                     NOT NULL VARCHAR2(15)
 MODIFICATION_NUM                NOT NULL NUMBER(10)
 CONFLICT_ID                     NOT NULL VARCHAR2(15)
 ASSET_NUM                       NOT NULL VARCHAR2(100)
 BUILD                                    VARCHAR2(30)
 COMMENTS                                 VARCHAR2(250)
 END_DT                                   DATE
 INSTALL_DT                               DATE
 INVLOC_ID                                VARCHAR2(15)
 METER_CNT                                NUMBER(22,7)
 NAME                                     VARCHAR2(100)
 OU_ADDR_ID                               VARCHAR2(15)
 OWNER_ACCNT_ID                           VARCHAR2(15)
 OWNER_ASSET_NUM                          VARCHAR2(50)
 PAR_ASSET_ID                             VARCHAR2(15)
 PER_ADDR_ID                              VARCHAR2(15)
 PROD_ID                                  VARCHAR2(15)
 PROD_INV_ID                              VARCHAR2(15)
 PR_CON_ID                                VARCHAR2(15)
 PR_EMP_ID                                VARCHAR2(15)
 QTY                                      NUMBER(22,7)
 REF_NUMBER_1                             VARCHAR2(30)
 REF_NUMBER_2                             VARCHAR2(30)
 REF_NUMBER_3                             VARCHAR2(30)
 REGISTERED_DT                            DATE
 SERIAL_NUM                               VARCHAR2(100)
 SHIP_DT                                  DATE
 START_DT                                 DATE
 STATUS_CD                                VARCHAR2(30)
 VERSION                                  VARCHAR2(30)
 WARRANTY_END_DT                          DATE
 WARRANTY_START_DT                        DATE
 WARRANTY_TYPE_CD                         VARCHAR2(30)
 X_CFG_LEVEL                              NUMBER(22,7)
 X_CMR_NUM                                VARCHAR2(15)
 X_CONTR_NUM                              VARCHAR2(15)
 X_INSTALL_DT                             DATE
 X_INSTALL_FLG                            CHAR(1)
 X_LEASE_EXP_DT                           DATE
 X_LEASE_NAME                             VARCHAR2(50)
 X_MACCSR_NAME                            VARCHAR2(15)
 X_PCSR_NAME                              VARCHAR2(15)
 X_PROD_ID                                VARCHAR2(50)
 X_SYSTEM_ID                              VARCHAR2(15)
 X_SOFT_REL_NUM                           VARCHAR2(15)
 

Tom Kyte
August 27, 2004 - 4:29 pm UTC

exactly because you are USING THE RBO!

in order to make the RBO better, you might well have to hint.

RBO - index unhappy

A reader, August 27, 2004 - 4:57 pm UTC

> exactly because you are USING THE RBO!

I'm not sure I understand.

1) Altering the session optimizer_goal to CHOOSE didn't help
2) Even with a predicate, it resorts to a FTS.

Tom Kyte
August 27, 2004 - 5:40 pm UTC

because it is the RBO and the rules it follows don't permit the index in this case.

You are using the RBO
The RBO isn't "smart" enough to see its way to using this index, it won't do it.

The RBO isn't smart enough to count rows via the index, not without some sort of predicate (and predicate would have to be on the column that is the leading edge of the index -- but since that column is NULLABLE you probably cannot even do that)

ops$tkyte@ORA9IR2> create table t ( x int, y int NOT NULL );
 
Table created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> create index t_idx on t(x,y);
 
Index created.
 
ops$tkyte@ORA9IR2> exec dbms_stats.set_table_stats( user, 'T', numrows=> 1000000, numblks => 10000 );
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> set autotrace traceonly explain
ops$tkyte@ORA9IR2> select /*+ RULE */ count(y) from t;
 
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'T'
 
 
 
ops$tkyte@ORA9IR2> select /*+ ALL_ROWS */ count(y) from t;
 
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: ALL_ROWS (Cost=5 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (FAST FULL SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=5 Card=1000000)
 
 
 
ops$tkyte@ORA9IR2> set autotrace off


ROW_ID, is this Siebel?  


 

RBO - index unhappy - SIEBEL

AR, August 27, 2004 - 6:34 pm UTC

Thank you for the detailed explanation. I have a followup question/clarification.

I made a typo in my previous message.
"3) Column PROD_ID is the leading column in unique concatenated index S_ASSET_U1_TEMP"

That should read - 
3) Column ASSET_NUM is the leading column..(as is demonstrated later in the same message).

"The RBO isn't smart enough to count rows via the index, not without some sort of predicate (and predicate would have to be on the column that is the leading edge of the index -- but since that column is NULLABLE you probably cannot even do that)"

I'm not sure if this applies here. ASSET_NUM is "NOT NULL". It is the leading column of the unique concatenated index. Even with a predicate, the query results in a FTS with a RBO.

SQL> l
  1* select ASSET_NUM from ar.S_ASSET where ASSET_NUM='1234'
SQL> /
no rows selected
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   TABLE ACCESS (FULL) OF 'S_ASSET'

Statistics
----------------------------------------------------------
          0  recursive calls
          2  db block gets
      11209  consistent gets
      11204  physical reads
          0  redo size
        171  bytes sent via SQL*Net to client
        294  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed


While when I do something as simple as the below, it nicely resorts to index range scan as expected. Am I not comparing apples to apples here? Why the different behaviour then?

SQL> create table t ( x varchar2(100) NOT NULL,y int);
Table created.
SQL> create unique index t_ind on t(x,y);
Index created.
SQL> select x from ar.t where x='1234';
no rows selected
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   INDEX (RANGE SCAN) OF 'T_IND' (UNIQUE)



Why did it do a FTS in the former, while a index range scan in the latter? x in the latter, and ASSET_NUM are both leading NOT NULL columns of a unique concatenated index.


PS : Yes, it is SIEBEL indeed! :-)). Why do you ask? Is the application notoriously famous?
 

Tom Kyte
August 27, 2004 - 7:31 pm UTC

prove to me that this

a) index exists
b) is usuable for a range scan such as you are trying to do.

the RBO would definitely use such an index. query the data dictionary and *prove it*

(or even better:

a) exp userid=u/p tables=this_table rows=n
b) imp userid=u/pd full=y show=y

and cut and paste the results)


oh yeah, siebel is "famous" -- why are they not involved in helping you get the performance from their application?

RBO - index unhappy - imp show=y

AR, August 27, 2004 - 7:56 pm UTC

Tom,
Please see below. Thank you for your time.

schaix21:/app/oracle> imp AR/pwd full=y show=y

Import: Release 7.3.4.0.0 - Production on Fri Aug 27 16:39:09 2004

Copyright (c) Oracle Corporation 1979, 1996. All rights reserved.


Connected to: Oracle7 Server Release 7.3.4.0.0 - Production
With the distributed and parallel query options
PL/SQL Release 2.3.4.0.0 - Production

Export file created by EXPORT:V07.03.04 via conventional path
. importing AR's objects into AR
"CREATE TABLE "S_ASSET" ("ROW_ID" VARCHAR2(15) NOT NULL, "CREATED" DATE DEFA"
"ULT sysdate NOT NULL, "CREATED_BY" VARCHAR2(15) NOT NULL, "LAST_UPD" DAT"
"E DEFAULT sysdate NOT NULL, "LAST_UPD_BY" VARCHAR2(15) NOT NULL, "MODIFI"
"CATION_NUM" NUMBER(10, 0) DEFAULT 0 NOT NULL, "CONFLICT_ID" VARCHAR2(15)"
" DEFAULT '0' NOT NULL, "ASSET_NUM" VARCHAR2(100) NOT NULL, "BUILD" VARCH"
"AR2(30), "COMMENTS" VARCHAR2(250), "END_DT" DATE, "INSTALL_DT" DATE, "INVLO"
"C_ID" VARCHAR2(15), "METER_CNT" NUMBER(22, 7), "NAME" VARCHAR2(100), "OU_AD"
"DR_ID" VARCHAR2(15), "OWNER_ACCNT_ID" VARCHAR2(15), "OWNER_ASSET_NUM" VARCH"
"AR2(50), "PAR_ASSET_ID" VARCHAR2(15), "PER_ADDR_ID" VARCHAR2(15), "PROD_ID""
" VARCHAR2(15), "PROD_INV_ID" VARCHAR2(15), "PR_CON_ID" VARCHAR2(15), "PR_EM"
"P_ID" VARCHAR2(15), "QTY" NUMBER(22, 7), "REF_NUMBER_1" VARCHAR2(30), "REF_"
"NUMBER_2" VARCHAR2(30), "REF_NUMBER_3" VARCHAR2(30), "REGISTERED_DT" DATE, "
""SERIAL_NUM" VARCHAR2(100), "SHIP_DT" DATE, "START_DT" DATE, "STATUS_CD" VA"
"RCHAR2(30), "VERSION" VARCHAR2(30), "WARRANTY_END_DT" DATE, "WARRANTY_START"
"_DT" DATE, "WARRANTY_TYPE_CD" VARCHAR2(30), "X_CFG_LEVEL" NUMBER(22, 7), "X"
"_CMR_NUM" VARCHAR2(15), "X_CONTR_NUM" VARCHAR2(15), "X_INSTALL_DT" DATE, "X"
"_INSTALL_FLG" CHAR(1), "X_LEASE_EXP_DT" DATE, "X_LEASE_NAME" VARCHAR2(50), "
""X_MACCSR_NAME" VARCHAR2(15), "X_PCSR_NAME" VARCHAR2(15), "X_PROD_ID" VARCH"
"AR2(50), "X_SYSTEM_ID" VARCHAR2(15), "X_SOFT_REL_NUM" VARCHAR2(15)) PCTFRE"
"E 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 STORAGE(INITIAL 73400320 NEXT 47185"
"920 MINEXTENTS 1 MAXEXTENTS 249 PCTINCREASE 50 FREELISTS 1 FREELIST GROUPS "
"1) TABLESPACE "TABLES""
"GRANT ALTER ON "S_ASSET" TO "SYS""
"GRANT DELETE ON "S_ASSET" TO "SYS""
"GRANT INDEX ON "S_ASSET" TO "SYS""
"GRANT INSERT ON "S_ASSET" TO "SYS""
"GRANT SELECT ON "S_ASSET" TO "SYS""
"GRANT UPDATE ON "S_ASSET" TO "SYS""
"GRANT REFERENCES ON "S_ASSET" TO "SYS""
"GRANT ALTER ON "S_ASSET" TO "SYSTEM""
"GRANT DELETE ON "S_ASSET" TO "SYSTEM""
"GRANT INDEX ON "S_ASSET" TO "SYSTEM""
"GRANT INSERT ON "S_ASSET" TO "SYSTEM""
"GRANT SELECT ON "S_ASSET" TO "SYSTEM""
"GRANT UPDATE ON "S_ASSET" TO "SYSTEM""
"GRANT REFERENCES ON "S_ASSET" TO "SYSTEM""
"GRANT ALTER ON "S_ASSET" TO "SIEBELP""
"GRANT DELETE ON "S_ASSET" TO "SIEBELP""
"GRANT INDEX ON "S_ASSET" TO "SIEBELP""
"GRANT INSERT ON "S_ASSET" TO "SIEBELP""
"GRANT SELECT ON "S_ASSET" TO "SIEBELP""
"GRANT UPDATE ON "S_ASSET" TO "SIEBELP""
"GRANT REFERENCES ON "S_ASSET" TO "SIEBELP""
"CREATE UNIQUE INDEX "S_ASSET_U1_TEMP" ON "S_ASSET" ("ASSET_NUM" , "PROD_ID""
" , "CONFLICT_ID" ) PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE (INITIAL 104"
"85760 NEXT 10485760 MINEXTENTS 1 MAXEXTENTS 249 PCTINCREASE 1 FREELISTS 1) "
"TABLESPACE "INDEXES""
Import terminated successfully without warnings.


Tom Kyte
August 27, 2004 - 8:05 pm UTC

something is up, if you do the following yourself, what do you see?

ops$tkyte@ORA734> CREATE TABLE "S_ASSET" ("ROW_ID" VARCHAR2(15) NOT NULL,
  2   "CREATED" DATE DEFAULT sysdate    NOT NULL,
  3   "CREATED_BY" VARCHAR2(15) NOT NULL,
  4   "LAST_UPD" DATE DEFAULT sysdate    NOT NULL,
  5   "LAST_UPD_BY" VARCHAR2(15) NOT NULL,
  6   "MODIFICATION_NUM" NUMBER(10,
  7   0) DEFAULT 0       NOT NULL,
  8   "CONFLICT_ID" VARCHAR2(15) DEFAULT '0'    NOT NULL,
  9   "ASSET_NUM" VARCHAR2(100) NOT NULL,
 10   "BUILD" VARCHAR2(30),
 11   "COMMENTS" VARCHAR2(250),
 12   "END_DT" DATE,
 13   "INSTALL_DT" DATE,
 14   "INVLOC_ID" VARCHAR2(15),
 15   "METER_CNT" NUMBER(22,
 16   7),
 17   "NAME" VARCHAR2(100),
 18   "OU_ADDR_ID" VARCHAR2(15),
 19   "OWNER_ACCNT_ID" VARCHAR2(15),
 20   "OWNER_ASSET_NUM" VARCHaR2(50),
 21  "PAR_ASSET_ID" VARCHAR2(15),
 22   "PER_ADDR_ID" VARCHAR2(15),
 23   "PROD_ID" VARCHAR2(15),
 24   "PROD_INV_ID" VARCHAR2(15),
 25   "PR_CON_ID" VARCHAR2(15),
 26   "PR_EMP_ID" VARCHAR2(15),
 27   "QTY" NUMBER(22,
 28   7),
 29   "REF_NUMBER_1" VARCHAR2(30),
 30   "REF_NUMBER_2" VARCHAR2(30),
 31   "REF_NUMBER_3" VARCHAR2(30),
 32   "REGISTERED_DT" DATE,
 33   "SERIAL_NUM" VARCHAR2(100),
 34   "SHIP_DT" DATE,
 35   "START_DT" DATE,
 36   "STATUS_CD" VARCHAR2(30),
 37   "VERSION" VARCHAR2(30),
 38   "WARRANTY_END_DT" DATE,
 39   "WARRANTY_START_DT" DATE,
 40   "WARRANTY_TYPE_CD" VARCHAR2(30),
 41   "X_CFG_LEVEL" NUMBER(22,
 42   7),
 43   "X_CMR_NUM" VARCHAR2(15),
 44   "X_CONTR_NUM" VARCHAR2(15),
 45   "X_INSTALL_DT" DATE,
 46   "X_INSTALL_FLG" CHAR(1),
 47   "X_LEASE_EXP_DT" DATE,
 48   "X_LEASE_NAME" VARCHAR2(50),
 49   "X_MACCSR_NAME" VARCHAR2(15),
 50   "X_PCSR_NAME" VARCHAR2(15),
 51   "X_PROD_ID" VARCHAR2(50),
 52   "X_SYSTEM_ID" VARCHAR2(15),
 53   "X_SOFT_REL_NUM" VARCHAR2(15))
 54  /
 
Table created.
 
ops$tkyte@ORA734>
ops$tkyte@ORA734> CREATE UNIQUE INDEX "S_ASSET_U1_TEMP" ON "S_ASSET" ("ASSET_NUM" ,
  2   "PROD_ID" ,
  3   "CONFLICT_ID" )
  4  /
 
Index created.
 
ops$tkyte@ORA734> set autotrace traceonly explain
ops$tkyte@ORA734> select ASSET_NUM from S_ASSET where ASSET_NUM='1234'
  2  /
 
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   INDEX (RANGE SCAN) OF 'S_ASSET_U1_TEMP' (UNIQUE)
 
 
 
ops$tkyte@ORA734> set autotrace off
ops$tkyte@ORA734>
 

RBO - index unhappy

AR, August 27, 2004 - 8:22 pm UTC

> something is up, if you do the following yourself, what do you see?

I see exactly what you see when I create a new table. Same thing. It comes back with a index range scan. What could be going on that causes a FTS on the other table? Aargh..

Tom Kyte
August 27, 2004 - 8:28 pm UTC

not a thing that I could think of -- not a thing.

not sure what to tell you at this point, if the index

a) exists
b) is usable
c) it would be used.

rule hint the query - what than?

RBO - index unhappy

AR, August 27, 2004 - 8:52 pm UTC

Okay, it works as expected now! I still don't know "why" though. I found another table called S_ASSET_BACKUP_BY_ANU in there. Those were the initials of the dba in the previous company. I dropped that table. And now when I retry, it uses an index range scan. Doesn't make any sense to me. I didn't bother to check synonyms/grants/indexes on the table before I dropped it. 

Obviously this had something to do with that table. But what? Seems a mystery. Maybe I should restore that table to find out..

SQL> l
  1  select ASSET_NUM from AR.S_ASSET where ASSET_NUM='1234'
  2*
SQL> /

no rows selected

Elapsed: 00:00:02.75

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   INDEX (RANGE SCAN) OF 'S_ASSET_U1_TEMP' (UNIQUE)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
        171  bytes sent via SQL*Net to client
        297  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed
 

The query does not use index in RBO even if the hint is included.

Sean, April 11, 2005 - 9:26 am UTC

Hi Tom,

Why does this query do full table scan on table t2 when there is a subquery? How can I avoid full table scan in this case.  Of course, four tables have different data in PROD.  It still refused to use index on t2 even I use hint.  We have to use RBO.  

Database version:  9204


Thanks so much for your help.

----------------------------------------------------

create table t1 as select * from sys.dba_objects;
create index t1_object_id_idx on t1(object_id);

create table t2 as select * from sys.dba_objects;
create index t2_object_id_idx on t2(object_id);

create table t3 as select * from sys.dba_objects;
create index t3_object_id_idx on t3(object_id);

create table t4 as select * from sys.dba_objects;
create index t4_object_id_idx on t4(object_id);

create or replace view v1(object_id) as 
    select t1.object_id
    from t1, t2
    where t1.object_id=t2.object_id
    union
    select t3.object_id
    from t3, t2
    where t3.object_id=t2.object_id;



SQL>explain plan for select * from v1
 where object_id=1;

SQL> @d:/oracle/ora92/rdbms/admin/utlxpls.sql

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

--------------------------------------------------------------------------
| Id  | Operation            |  Name             | Rows  | Bytes | Cost  |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |                   |       |       |       |
|   1 |  VIEW                | V1                |       |       |       |
|   2 |   SORT UNIQUE        |                   |       |       |       |
|   3 |    UNION-ALL         |                   |       |       |       |
|   4 |     NESTED LOOPS     |                   |       |       |       |
|*  5 |      INDEX RANGE SCAN| T1_OBJECT_ID_IDX  |       |       |       |
|*  6 |      INDEX RANGE SCAN| T2_OBJEC_ID_IDX   |       |       |       |
|   7 |     NESTED LOOPS     |                   |       |       |       |
|*  8 |      INDEX RANGE SCAN| T3_OBJECT_ID_IDX  |       |       |       |
|*  9 |      INDEX RANGE SCAN| T2_OBJEC_ID_IDX   |       |       |       |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access("T1"."OBJECT_ID"=1)
   6 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
   8 - access("T3"."OBJECT_ID"=1)
   9 - access("T3"."OBJECT_ID"="T2"."OBJECT_ID")

Note: rule based optimization

25 rows selected.

SQL> truncate table plan_table;

Table truncated.


SQL> explain plan for select * from v1
  2  where object_id in (select object_id from t4
  3                      where object_id=1);

Explained.

SQL> @d:/oracle/ora92/rdbms/admin/utlxpls.sql

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

-----------------------------------------------------------------------------
| Id  | Operation               |  Name             | Rows  | Bytes | Cost  |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |                   |       |       |       |
|   1 |  MERGE JOIN             |                   |       |       |       |
|   2 |   SORT JOIN             |                   |       |       |       |
|   3 |    VIEW                 | V1                |       |       |       |
|   4 |     SORT UNIQUE         |                   |       |       |       |
|   5 |      UNION-ALL          |                   |       |       |       |
|   6 |       NESTED LOOPS      |                   |       |       |       |
|   7 |        TABLE ACCESS FULL| T2                |       |       |       |
|*  8 |        INDEX RANGE SCAN | T1_OBJECT_ID_IDX  |       |       |       |
|   9 |       NESTED LOOPS      |                   |       |       |       |
|  10 |        TABLE ACCESS FULL| T2                |       |       |       |
|* 11 |        INDEX RANGE SCAN | T3_OBJECT_ID_IDX  |       |       |       |
|* 12 |   SORT JOIN             |                   |       |       |       |
|  13 |    VIEW                 | VW_NSO_1          |       |       |       |
|  14 |     SORT UNIQUE         |                   |       |       |       |
|* 15 |      INDEX RANGE SCAN   | T4_OBJECT_ID_IDX  |       |       |       |
-----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   8 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
  11 - access("T3"."OBJECT_ID"="T2"."OBJECT_ID")
  12 - access("V1"."OBJECT_ID"="VW_NSO_1"."$nso_col_1")
       filter("V1"."OBJECT_ID"="VW_NSO_1"."$nso_col_1")
  15 - access("T4"."OBJECT_ID"=1)

Note: rule based optimization

32 rows selected.

SQL>  

Tom Kyte
April 11, 2005 - 9:31 am UTC

simply by using the much smarter CBO with statistics it doesn't full scan. the RBO isn't "as smart".

Also, are you *sure* you need the expensive UNION, could you in real life use UNION ALL?



old 1: explain plan for &1
new 1: explain plan for select * from v1 where object_id in (select object_id from t4 where object_id=1)

Explained.


PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------

------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 18 | 55 |
| 1 | NESTED LOOPS SEMI | | 1 | 18 | 55 |
| 2 | VIEW | V1 | 2 | 26 | 54 |
| 3 | SORT UNIQUE | | 2 | 20 | 54 |
| 4 | UNION-ALL | | | | |
| 5 | MERGE JOIN CARTESIAN| | 1 | 10 | 2 |
|* 6 | INDEX RANGE SCAN | T1_OBJECT_ID_IDX | 1 | 5 | 1 |
| 7 | BUFFER SORT | | 1 | 5 | 1 |
|* 8 | INDEX RANGE SCAN | T2_OBJECT_ID_IDX | 1 | 5 | 1 |
| 9 | MERGE JOIN CARTESIAN| | 1 | 10 | 2 |
|* 10 | INDEX RANGE SCAN | T3_OBJECT_ID_IDX | 1 | 5 | 1 |
| 11 | BUFFER SORT | | 1 | 5 | 1 |
|* 12 | INDEX RANGE SCAN | T2_OBJECT_ID_IDX | 1 | 5 | 1 |
|* 13 | INDEX RANGE SCAN | T4_OBJECT_ID_IDX | 1 | 5 | 1 |
------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

6 - access("T1"."OBJECT_ID"=1)
8 - access("T2"."OBJECT_ID"=1)
10 - access("T3"."OBJECT_ID"=1)
12 - access("T2"."OBJECT_ID"=1)
13 - access("T4"."OBJECT_ID"=1)
filter("V1"."OBJECT_ID"="T4"."OBJECT_ID")

Note: cpu costing is off

31 rows selected.


View created.


14 rows deleted.

old 1: explain plan for &1
new 1: explain plan for select * from v1 where object_id in (select object_id from t4 where object_id=1)

Explained.


PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 3 |
|* 1 | HASH JOIN SEMI | | 1 | 10 | 3 |
| 2 | VIEW | V1 | 565 | 2825 | 1 |
| 3 | UNION-ALL PARTITION| | | | |
| 4 | NESTED LOOPS | | 1 | 10 | 2 |
|* 5 | INDEX RANGE SCAN | T1_OBJECT_ID_IDX | 1 | 5 | 1 |
|* 6 | INDEX RANGE SCAN | T2_OBJECT_ID_IDX | 1 | 5 | 1 |
| 7 | NESTED LOOPS | | 1 | 10 | 2 |
|* 8 | INDEX RANGE SCAN | T3_OBJECT_ID_IDX | 1 | 5 | 1 |
|* 9 | INDEX RANGE SCAN | T2_OBJECT_ID_IDX | 1 | 5 | 1 |
|* 10 | INDEX RANGE SCAN | T4_OBJECT_ID_IDX | 1 | 5 | 1 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - access("V1"."OBJECT_ID"="T4"."OBJECT_ID")
5 - access("T1"."OBJECT_ID"=1)
6 - access("T2"."OBJECT_ID"=1)
filter("T1"."OBJECT_ID"="T2"."OBJECT_ID")
8 - access("T3"."OBJECT_ID"=1)
9 - access("T2"."OBJECT_ID"=1)
filter("T3"."OBJECT_ID"="T2"."OBJECT_ID")
10 - access("T4"."OBJECT_ID"=1)

Note: cpu costing is off

30 rows selected.


Qucik question please Tom,

RD, June 11, 2006 - 7:55 pm UTC

Hi,
I am searching for an answer for the following question, but in the thread there is correspondence for only hints with RBO:-

Suppose we are using optimizer_mode = RULE, version is 8.1.7.0.
Someone goes and analyzes all the tables. Will there be any affect on the optimizer?

Regards,
RD


Tom Kyte
June 12, 2006 - 9:48 am UTC

if the optimizer mode is set to rule, then the CBO will not be used anymore than it already was (eg: it was being used if you use any index organized tables, partitioned tables, .... the presense of correct statistics may will change plans against those)



How to ingore RULE hint without re-write

.NET developer, July 15, 2011 - 9:07 am UTC

Hi Tom

We have a third party vendor product that's using RULE hint in a SELECT SQL. We confirmed that removing RULE hint made the SQL run much faster. Any feature that we can use to force oracle to ignore RULE hint or re-write the SQL excluding RULE hint ? Vendor changes are almost not an option currently.

We explored stored outlines feature a bit, but my understanding so far is that this feature is helpful to override oracle execution plans with a user defined execution plan for the same SQL, but not to re-write SQL1 as SQL2. Any comments ?

tuning queries in rbo

A reader, July 19, 2011 - 11:18 am UTC

One of our db still uses RBO optimizer mode. IN case,we run across slow performing queries,what would be right approach to tune them? I can only think we could try changing join order by changing the table order following the from clause.

Please suggest any other methods if available (if we cant change to CBO mode).Oracle version is 9i
Tom Kyte
July 19, 2011 - 1:17 pm UTC

you can change to CBO - just put a dynamic_sampling(3) hint in there. I'd rather that then try to fight the RBO.

You'd have to restore to old old tricks like knowing that the tables are processed typically from right to left (driving order), and the where clause typically from the bottom up, and that in order to avoid a specific index - you would || '' to a string or +0 to a number or a date.

Lots of tricks that will make it really hard to move to the CBO later since your SQL will be all whacked out.

You rock Tom!

.NET Developer, July 22, 2011 - 9:49 am UTC

Thanks for your link to DBMS_ADVANCED_REWRITE, was exactly what I was looking for.

More to Explore

Performance

Get all the information about database performance in the Database Performance guide.