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 !
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)
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.
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?
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?
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.
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
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.
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.
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,
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...
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
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
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)
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.
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?
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.
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..
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>
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
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 ?
July 18, 2011 - 9:37 am UTC
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
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.