CBO hints
hi, please give some links concerning CBO
documentation,experiences,...
thanks Michael
One alternative is having the hints in functions/packages in DB, if possible.
Similar Messages
-
Using CBO hints in Oracle reports 6i
Hi everyone,
I'm trying to add a simple hint to my a query defined in the datamodel of my report. The hint never shows up in v$sql at execution time.
I have a stong feeling that report chops the hint out because reports thinks it's a comment like any other comment.
Is there a way I can put hints in queries of Oracle Report 6i ????
Best regards,
Guillaume Goulet-VallièresOne alternative is having the hints in functions/packages in DB, if possible.
-
How to comment my SQL in PL/SQL so that the comment carries to AWR?
Is there anything about using comments as in /* some comment */ in a SQL statement in a PL SQL procedure and expecting that to be carried thru to AWR?
Right now it seems my comments are not visible in the AWR history. I'm wondering if they are compiled out when they are in PL/SQL blocks or not?
Is there a way to preserve them such that they remain without using dynamic sql?steffi wrote:
Is there anything about using comments as in /* some comment */ in a SQL statement in a PL SQL procedure and expecting that to be carried thru to AWR?Comments are stripped from (static) SQL code written in PL/SQL, unless that comment is a CBO hint.
It is a "+Good Thing+" as it reduces the memory footprint of the cursor in the shared pool. It also ensures a greater potential degree of cursor sharing as the very same SQL statement with different comments will wind up as a single re-usable cursor.
Why exactly do you want to see comments in SQL cursors in AWR reports? This is just a high level overview - not sure what enlightenment SQL comments will bring. -
SQL Area Usage, Performance Problem
Hi,
I'm a software engineer, not a DBA, with a lot of doubts about our production environment, but I think that I have found a problem at our production Database.
At our development database when I execute the same SQL statement over than onces, I can see this behaviour (for example):
* First execution 580 milisenconds.
* Second execution 21 milisencons.
As where I know, I understand that the compiled SQL statement is stored at the SQL Area, and by that reason the second execution is faster then the first one. Is that assumption correct?
If it is correct, I have a problem on our production Database because it does not work as expected. I have done a lot of trials, and SQL statement executions do not reduce her time execution when I do consecutive SQL execution.
Could you help me? I think that the parameter shared_pool_size value is too lower for our production server.
Thanks in advance.
Best Regards,
JoanJust a comment about performance tuning and troubleshooting in general Joan.
It is very dangerous to base your conclusions on observation only. Consider the following example:
SQL> set timing on
SQL> select count(*) from all_objects;
COUNT(*)
10296
Elapsed: 00:00:18.38
SQL>
SQL> -- now using the magically warp speed hint
SQL> select /*+ WARP_SPEED */ count(*) from all_objects;
COUNT(*)
10296
Elapsed: 00:00:00.32
SQL>
From 18 seconds to less than half a second. It does look, based on pure observation of the results, that there is a WARP_SPEED hint in Oracle and it does increase performance dramatically. And we can also infer that this is an undocumented feature added by one of the Oracle kernel developers that is also a Star Trek fan. ;-)
But if we turn on SQL tracing (as suggested), we will see that the first SELECT did a lot of physical I/O. The 2nd SELECT did the same work, but without having to do the very expensive physical I/O - as the data blocks it need to hit (again) was now in memory (inside Oracle's buffer cache).
It had nothing to do with an invalid and non-existing CBO hint called WARP_SPEED.
The critical bit is KNOWING exactly what you are measuring when using this type of approach. If you do not know that, you are in no position to determine a sound and valid conclusion.
Side note on shared pool size - one of the worse mistakes can be to increase it. It can cause incredible damage to performance on an instance that deals with bindless/non-sharable SQL as the pool that Oracle needs to use to determine if a soft parse is possible gets to be huge.. without the benefit of being able to soft parse and forced to hard parse anyway. And that hard parse also now added to the size of the pool. -
Hi,
I have a query which is using MERGE JOIN CARTESIAN (see step 4 and 11 in execution plan bellow) and i believe this is the culprit and query is never returning. Stats are up to date on all tables. This query involves two remote databases which are accessed using VIEWs from local database. Is there any hint which i can use here to avoid merge join Cartesian and test. Thanks. Also pasting query if any one can have any idea.
Select distinct
substr(Revenue.FKDAT,5,2) || '/' || substr(Revenue.FKDAT,7,2) || '/' || substr(Revenue.FKDAT,1,4) INVOICE_DATE
, Revenue.VBELN INVOICE_NR
, Revenue.ARKTX CUSTDEVICE
, (case Revenue.ZU_LOTID
when null then hstg_partinfo_cust_dw.CUSTPARTOUT
when '' then hstg_partinfo_cust_dw.CUSTPARTOUT
Else
(case decode(trim(zodss1b2.biztype),'TNR-E330-BAKE','TNR','')
when 'TNR' then
(case when (select CUSTPARTOUT from hstg_partinfo_cust_dw Where lotid = 'U' || substr(Revenue.ZU_LOTID,2,7) || '.' || substr(Revenue.ZU_LOTID,9) and stage = 'T998-TFIN' and rownum = 1)
is null then (select CUSTPARTOUT from hstg_partinfo_cust_dw Where lotid = (select parentid from hstg_partinfo_cust_dw where lotid = 'U' || substr(Revenue.ZU_LOTID,2,7) || '.' || substr(Revenue.ZU_LOTID,9) and stage = 'E330-BAKE' and rownum = 1) and stage = 'T998-TFIN' and rownum = 1)
else (select CUSTPARTOUT from hstg_partinfo_cust_dw Where lotid = 'U' || substr(Revenue.ZU_LOTID,2,7) || '.' || substr(Revenue.ZU_LOTID,9) and stage = 'T998-TFIN' and rownum = 1)
end)
else hstg_partinfo_cust_dw.CUSTPARTOUT
end)
end) CUSTPARTOUT
, Revenue.PONUM DO_NR
, Revenue.BSTKD PO_NR
, Revenue.FKIMG GD_QTY
, Revenue.zkbetr GD_PRICE
, Revenue.NETWR GD_AMT
, '' REJ_QTY
, '' REJ_PRICE
, '' REJ_AMT
, Revenue.ZU_LOTID LOTTYPE
,(case Revenue.ZU_LOTID
when null then substr(Revenue.ZU_PRTID,1,instr(Revenue.ZU_PRTID,'.') -1)
when '' then substr(Revenue.ZU_PRTID,1,instr(Revenue.ZU_PRTID,'.') -1)
Else
(case decode(trim(zodss1b2.biztype),'TNR-E330-BAKE','TNR','')
when 'TNR' then
(case when (select substr(PARTID,1,instr(PARTID,'.') -1) from hstg_partinfo_cust_dw Where lotid = 'U' || substr(Revenue.ZU_LOTID,2,7) || '.' || substr(Revenue.ZU_LOTID,9) and stage = 'T998-TFIN' and rownum = 1)
is null then (select substr(PARTID,1,instr(PARTID,'.') -1) from hstg_partinfo_cust_dw Where lotid = (select parentid from hstg_partinfo_cust_dw where lotid = 'U' || substr(Revenue.ZU_LOTID,2,7) || '.' || substr(Revenue.ZU_LOTID,9) and stage = 'E330-BAKE' and rownum = 1) and stage = 'T998-TFIN' and rownum = 1)
else (select substr(PARTID,1,instr(PARTID,'.') -1) from hstg_partinfo_cust_dw Where lotid = 'U' || substr(Revenue.ZU_LOTID,2,7) || '.' || substr(Revenue.ZU_LOTID,9) and stage = 'T998-TFIN' and rownum = 1)
end)
else substr(Revenue.ZU_PRTID,1,instr(Revenue.ZU_PRTID,'.') -1)
end)
end) PARTID
, '' TESTER
, substr(zodss1b2.BSTDK,5,2) || '/' || substr(zodss1b2.BSTDK,7,2) || '/' || substr(zodss1b2.BSTDK,1,4) DO_DATE
, hstg_partinfo_cust_dw.package package
, hstg_partinfo_cust_dw.pincount pincount
, (select c.catg07 from ops$ods_adm.catg c where c.partid=revenue.zu_prtid) as package_size
, (select c.catg09 from ops$ods_adm.catg c where c.partid=revenue.zu_prtid) as wafer_size
, decode(trim(zodss1b2.biztype),'TNR-E330-BAKE','TNR','') BIZTYPE
--from revenue, zodss1b2,dim_partinfo
from revenue, zodss1b2,hstg_partinfo_cust_dw
where Revenue.Remarks <> 'CANCELLED'
and Revenue.FKART in ('ZUF2')
and Revenue.KUNNR in ('BC1001','BC1002','BC1003')
and Revenue.FKDAT like '201008%'
and Revenue.FKIMG > 0
and Revenue.kpein > 0
and Revenue.ZU_LOTID = zodss1b2.charg
and Revenue.MATNR = zodss1b2.matnr
--and Revenue.ZU_PRTID = dim_partinfo.partid
and hstg_partinfo_cust_dw.lotid='U' || substr(Revenue.ZU_LOTID,2,7) || '.' || substr(Revenue.ZU_LOTID,9)
Union all
Select distinct
substr(Revenue.FKDAT,5,2) || '/' || substr(Revenue.FKDAT,7,2) || '/' || substr(Revenue.FKDAT,1,4) INVOICE_DATE
, Revenue.VBELN INVOICE_NR
, Revenue.ARKTX CUSTDEVICE
, hstg_partinfo_cust_dw.CUSTPARTOUT CUSTPARTOUT
, Revenue.PONUM DO_NR
, Revenue.BSTKD PO_NR
, Revenue.FKIMG GD_QTY
, Revenue.zkbetr GD_PRICE
, Revenue.NETWR GD_AMT
, '' REJ_QTY
, '' REJ_PRICE
, '' REJ_AMT
, Revenue.ZU_LOTID LOTTYPE
, substr(Revenue.ZU_PRTID,1,instr(Revenue.ZU_PRTID,'.') -1) PARTID
, '' TESTER
, substr(zodsscrap.BSTDK,5,2) || '/' || substr(zodsscrap.BSTDK,7,2) || '/' || substr(zodsscrap.BSTDK,1,4) DO_DATE
, hstg_partinfo_cust_dw.package package
, hstg_partinfo_cust_dw.pincount pincount
, (select c.catg07 from ops$ods_adm.catg c where c.partid=revenue.zu_prtid) as package_size
, (select c.catg09 from ops$ods_adm.catg c where c.partid=revenue.zu_prtid) as wafer_size
--from revenue, zodsscrap,dim_partinfo
from revenue, zodsscrap,hstg_partinfo_cust_dw
where Revenue.Remarks <> 'CANCELLED'
and Revenue.FKART in ('ZUF2')
and Revenue.KUNNR in ('BC1001','BC1002','BC1003')
and Revenue.FKDAT like '201008%'
and Revenue.FKIMG > 0
and Revenue.kpein > 0
and Revenue.ZU_LOTID = zodsscrap.charg
--and Revenue.ZU_PRTID = dim_partinfo.partid
and hstg_partinfo_cust_dw.lotid='U' || substr(Revenue.ZU_LOTID,2,7) || '.' || substr(Revenue.ZU_LOTID,9);hstg_partinfo_cust_dw is a view which is on remote database USGDWDBP and is queried using synonym in the local database.
revenue is also a view which is in another remote database which is queried using synonym in local database.
ODS Plan
| Id | Operation | Name | Rows | Bytes | Cost | Inst |IN-OUT|
| 0 | SELECT STATEMENT | | 2 | 8532 | 999 | | |
| 1 | UNION-ALL | | | | | | |
| 2 | SORT UNIQUE | | 1 | 4277 | 985 | | |
| 3 | NESTED LOOPS | | 1 | 4277 | 983 | | |
| 4 | MERGE JOIN CARTESIAN| | 1 | 4107 | 980 | | |
| 5 | REMOTE | | 1 | 4076 | 2 | USGDW~ | R->S |
| 6 | BUFFER SORT | | 918K| 27M| 978 | | |
| 7 | TABLE ACCESS FULL | ZODSS1B2 | 918K| 27M| 978 | | |
| 8 | REMOTE | | 1 | 170 | 3 | SAPP0~ | R->S |
| 9 | SORT UNIQUE | | 1 | 4255 | 14 | | |
| 10 | NESTED LOOPS | | 1 | 4255 | 12 | | |
| 11 | MERGE JOIN CARTESIAN| | 1 | 4096 | 9 | | |
| 12 | REMOTE | | 1 | 4076 | 2 | USGDW~ | R->S |
| 13 | BUFFER SORT | | 1755 | 35100 | 7 | | |
| 14 | TABLE ACCESS FULL | ZODSSCRAP | 1755 | 35100 | 7 | | |
| 15 | REMOTE | | 1 | 159 | 3 | SAPP0~ | R->S |
---------------------------------------------------------------------------------------Thanks in advance
SalmanDear Salman Qureshi,
First of all please see Jonathan Lewis' post about the merge join cartesian;
http://jonathanlewis.wordpress.com/2006/12/13/cartesian-merge-join/
You may also check the Thomas Kyte's thread;
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4105951726381
You merge join cartesian cost is so low that the impact will not be that high.
Yes, there are ways to avoid the merge join cartesian. First you have to check the where conditions. Merge join cartesian steps are happening because of the bad SQL algorithm OR sometimes Oracle needs to have a merge join cartesian. At the end, it is up to your SQL.
After than that, you can also use the ORDERED CBO hint to avoid the merge join cartesian.
Please add below line after the SELECT clause and see the new execution plan;
/*+ ORDERED */I hope your problem will be fixed when you show the CBO to use the ORDERED Oracle hint.
About the ORDERED Oracle hint, please check it out;
http://download.oracle.com/docs/cd/B14117_01/server.101/b10752/hintsref.htm#5555
ORDERED
The ORDERED hint causes Oracle to join tables in the order in which they appear in the FROM clause.
If you omit the ORDERED hint from a SQL statement performing a join, then the optimizer chooses the order in which to join the tables. You might want to use the ORDERED hint to specify a join order if you know something about the number of rows selected from each table that the optimizer does not. Such information lets you choose an inner and outer table better than the optimizer could.Hope That Helps.
Ogan -
Refcursor taking a lot of time to print results
Hi Gurus,
I have a table with name kemp with columns
kemp_ID NOT NULL NUMBER(10)
ACRONYM CLOB
TERM CLOB
DEFINITION CLOB When I do a query on table
select * from kemp ; results are getting displayed very soon lesser than *1.5 seconds* But when I write a procedure to return the results using refcursor
create procedure pr_retall(cv_1 in out sys_refcursor)
as
begin
open cv_1 for select * from kemp ;
end;
set serveroutput on size unlimited;
var x1 refcursor;
exec pr_retall(:x1);
print x1;This is taking around *7 seconds* for getting displayed.
Please tell me know ways to tune it. I want to reduce the time it is consuming.
Thanks & Regards,
Vikas KrishnaVikas Krishna wrote:
Please find the timing.
<snipped>Using elapsed time (as you did) to measure and benchmark performance is wrong.
Why? Because the test2 is seldom, if ever given how these tests are usually done, executed using the very same conditions that test1 was executed in.
As conditions differ between tests, elapsed time differs. As conditions differ, you cannot compare test1 with test2. I gave an example of how wrong this is in thread {message:id=4333700} - where physical I/O slows down test1 and logical I/O speeds up test2. And how this can be used to substantiate the erroneous conclusion that the non-existing CBO hint warp_drive increased the performance of test2.
If you want to benchmark test1 against test2, you need to know in exact detail how each test technically works.. need to know how to ensure that the commonality between the two tests remain constant for all tests and how to measure only those parts that are different between tests.
And no, benchmarking is not that complex. It is in fact even more complex than this. Which is why it is best left to those who know how to benchmark.. on behalf of those that can find some meaning in the result of that benchmark.
For the majority of us working and developing in the real world, benchmarking is achieving a useless answer to a meaningless question for a nonsensical issue. Utterly useless... -
HINTS with CBO Optimizer?
Hi Experts,
First of all Merry Chrismas to all ...
I am confusing in the area of HInts.. My question in that are we using any Hints when Optimizer is CBO ?( i am using oracle 10g r2)
bcoz some dba of my project are saying dont ever user Hints if optimizer is CBO. It will Handle the query and will give you most efficient execution plan.
can you please guide me about this? and my another question is that if I am writting a select statement and it involve the join of 7 tables(assume)
then is it necessary to have the proper order of table and where clause if i am using CBO optimizer?
Thanks in advance,
Prashant1. Your DBA is right. Using Optimizer hints is the path of last resort.
See: Ask Tom &quot;Hints When to use&quot;
2. No need to worry about the order of tables, CBO is smart enough to figure that out.
Suggest you read more about it here:
https://blogs.oracle.com/optimizer/
Hinting | Oracle Scratchpad -
CBO refuses hint (sometimes)
Sometimes, the CBO does not take hints. Can anyone explain it?
SQL:
SELECT * FROM v_osi_child v1 WHERE mainuuid IN (SELECT uuid FROM ot_sys_index_org v2 WHERE mainuuid IN(:p1))v_osi_child is a view which union-alls about 50 tables. ot_sys_index_org is here only used for getting some mainuuids.
The statement itself is free of sense :)
Explain plan:
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 4 | 17252 | 402K (1)| 01:20:31 |
|* 1 | HASH JOIN | | 4 | 17252 | 402K (1)| 01:20:31 |
|* 2 | INDEX RANGE SCAN | PK_OTSYSINDEX | 4 | 340 | 4 (0)| 00:00:01 |
| 3 | VIEW | V_OSI_CHILD | 56M| 223G| 402K (1)| 01:20:27 |
| 4 | UNION-ALL | | | | | |
| 5 | TABLE ACCESS FULL| FL_ALLG_BENUTZERDATEN | 16797 | 1295K| 105 (0)| 00:00:02 |
| 6 | TABLE ACCESS FULL| FL_BERATUNG | 17 | 1343 | 3 (0)| 00:00:01 |
| 7 | TABLE ACCESS FULL| PA_KBIVBI | 16797 | 1295K| 105 (0)| 00:00:02 |
...This statement needs more than 10 minutes.
Inserting a hint:
SELECT /*+ push_pred(v1) */ * FROM v_osi_child v1 WHERE mainuuid IN (SELECT uuid FROM ot_sys_index_org v2 WHERE mainuuid IN(:p1));Explain plan:
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 4 | 17852 | 12747 (1)| 00:02:33 |
| 1 | NESTED LOOPS | | 4 | 17852 | 12747 (1)| 00:02:33 |
|* 2 | INDEX RANGE SCAN | PK_OTSYSINDEX | 4 | 532 | 4 (0)| 00:00:01 |
| 3 | VIEW | V_OSI_CHILD | 1 | 4330 | 3186 (1)| 00:00:39 |
| 4 | UNION ALL PUSHED PREDICATE | | | | | |
| 5 | TABLE ACCESS BY INDEX ROWID| FL_ALLG_BENUTZERDATEN | 1 | 79 | 2 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | OSI_FLALLGBENUTZE_MAINUUID | 1 | | 1 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| FL_BERATUNG | 1 | 79 | 2 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | OSI_FLBERATUNG_MAINUUID | 1 | | 1 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY INDEX ROWID| PA_KBIVBI | 1 | 79 | 2 (0)| 00:00:01 |
|* 10 | INDEX RANGE SCAN | OSI_PAKBIVBI_MAINUUID | 1 | | 1 (0)| 00:00:01 |
| 11 | TABLE ACCESS BY INDEX ROWID| FL_K_AKTIVITAET | 1 | 100 | 2 (0)| 00:00:01 |
|* 12 | INDEX RANGE SCAN | OSI_FLKAKTIVITAET_MAINUUID | 1 | | 1 (0)| 00:00:01 |
...Well, using the index on mainuuids is realy a good idea, if the estimated resultset of the subquery are about 4 rows.
Costs are decreased from 402k to 12747.
Query runs in a second.
Now, lets modify the subquery a little bit. Again, the estimated result of the subquery are 4 rows:
SELECT * FROM v_osi_child v1 WHERE mainuuid IN (SELECT mainuuid FROM ot_sys_index_org v2 WHERE value IN(:p1));Explain plan:
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 4 | 17104 | 402K (1)| 01:20:31 |
|* 1 | HASH JOIN RIGHT SEMI| | 4 | 17104 | 402K (1)| 01:20:31 |
|* 2 | INDEX RANGE SCAN | IX_OT_SYS_INDEX01 | 4 | 192 | 5 (0)| 00:00:01 |
| 3 | VIEW | V_OSI_CHILD | 56M| 223G| 402K (1)| 01:20:27 |
| 4 | UNION-ALL | | | | | |
| 5 | TABLE ACCESS FULL| FL_ALLG_BENUTZERDATEN | 16797 | 1295K| 105 (0)| 00:00:02 |
| 6 | TABLE ACCESS FULL| FL_BERATUNG | 17 | 1343 | 3 (0)| 00:00:01 |
| 7 | TABLE ACCESS FULL| PA_KBIVBI | 16797 | 1295K| 105 (0)| 00:00:02 |
| 8 | TABLE ACCESS FULL| FL_K_AKTIVITAET | 26 | 2600 | 3 (0)| 00:00:01 |
...Well, almost the same values...
Now, use the same hint:
SELECT /*+ push_pred(v1) */ * FROM v_osi_child v1 WHERE mainuuid IN (SELECT mainuuid FROM ot_sys_index_org v2 WHERE value IN(:p1));Explain plan:
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 4 | 17104 | 402K (1)| 01:20:31 |
|* 1 | HASH JOIN RIGHT SEMI| | 4 | 17104 | 402K (1)| 01:20:31 |
|* 2 | INDEX RANGE SCAN | IX_OT_SYS_INDEX01 | 4 | 192 | 5 (0)| 00:00:01 |
| 3 | VIEW | V_OSI_CHILD | 56M| 223G| 402K (1)| 01:20:27 |
| 4 | UNION-ALL | | | | | |
| 5 | TABLE ACCESS FULL| FL_ALLG_BENUTZERDATEN | 16797 | 1295K| 105 (0)| 00:00:02 |
| 6 | TABLE ACCESS FULL| FL_BERATUNG | 17 | 1343 | 3 (0)| 00:00:01 |
| 7 | TABLE ACCESS FULL| PA_KBIVBI | 16797 | 1295K| 105 (0)| 00:00:02 |
...Nothing has changed.
I can't explain it. It seems to be an oracle-bug...
Release: 11.1.0.7.0 - 64bit Production
OS: RHEL 5.3Ulrich Weiss wrote:
All this is readable from the explain plans.I don't know the reason behind this behaviour and I don't have a 11g version to test on, but I found similar observations on a 10g database. A small yet complete reproducible test case always helps. Here is mine:
SQL> select * from v$version ;
BANNER
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE 10.2.0.4.0 Production
TNS for Solaris: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production
SQL> drop view v_t ;
View dropped.
SQL> drop table t1 purge ;
Table dropped.
SQL> drop table t purge ;
Table dropped.
SQL> create table t nologging as select * from all_objects ;
Table created.
SQL> create table t1 nologging as select * from t where rownum <= 1000 ;
Table created.
SQL> create or replace view v_t as
2 select * from t where object_type = 'PACKAGE'
3 union all
4 select * from t where object_type = 'PACKAGE BODY' ;
View created.
SQL> explain plan for select * from v_t where object_id in (select object_id from t1 where owner = '
2 SYS') ;
Explained.
SQL> exec dbms_stats.gather_table_stats(user, 'T') ;
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats(user, 'T1') ;
PL/SQL procedure successfully completed.
SQL> explain plan for select * from v_t where object_id in (select object_id from t1 where owner = 'SYS') ;
Explained.
SQL> select * from table(dbms_xplan.display) ;
PLAN_TABLE_OUTPUT
Plan hash value: 443534535
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 2 | 274 | 333 (2)| 00:00:04 |
|* 1 | HASH JOIN RIGHT SEMI| | 2 | 274 | 333 (2)| 00:00:04 |
|* 2 | TABLE ACCESS FULL | T1 | 893 | 8037 | 5 (0)| 00:00:01 |
| 3 | VIEW | V_T | 2055 | 256K| 327 (2)| 00:00:04 |
| 4 | UNION-ALL | | | | | |
|* 5 | TABLE ACCESS FULL| T | 1067 | 97K| 164 (2)| 00:00:02 |
|* 6 | TABLE ACCESS FULL| T | 988 | 92872 | 164 (2)| 00:00:02 |
Predicate Information (identified by operation id):
1 - access("OBJECT_ID"="OBJECT_ID")
2 - filter("OWNER"='SYS')
5 - filter("OBJECT_TYPE"='PACKAGE')
6 - filter("OBJECT_TYPE"='PACKAGE BODY')
21 rows selected.
SQL> explain plan for select /*+ push_pred(v_t) */ * from v_t where object_id in (select object_id from t1 where owner = 'SYS') ;
Explained.
SQL> select * from table(dbms_xplan.display) ;
PLAN_TABLE_OUTPUT
Plan hash value: 443534535
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 2 | 274 | 333 (2)| 00:00:04 |
|* 1 | HASH JOIN RIGHT SEMI| | 2 | 274 | 333 (2)| 00:00:04 |
|* 2 | TABLE ACCESS FULL | T1 | 893 | 8037 | 5 (0)| 00:00:01 |
| 3 | VIEW | V_T | 2055 | 256K| 327 (2)| 00:00:04 |
| 4 | UNION-ALL | | | | | |
|* 5 | TABLE ACCESS FULL| T | 1067 | 97K| 164 (2)| 00:00:02 |
|* 6 | TABLE ACCESS FULL| T | 988 | 92872 | 164 (2)| 00:00:02 |
Predicate Information (identified by operation id):
1 - access("OBJECT_ID"="OBJECT_ID")
2 - filter("OWNER"='SYS')
5 - filter("OBJECT_TYPE"='PACKAGE')
6 - filter("OBJECT_TYPE"='PACKAGE BODY')
21 rows selected.
SQL> explain plan for select v_t.* from v_t, t1 where v_t.object_id = t1.object_id and t1.owner = 'SYS' ;
Explained.
SQL> select * from table(dbms_xplan.display) ;
PLAN_TABLE_OUTPUT
Plan hash value: 2725479221
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1834 | 245K| 333 (2)| 00:00:04 |
|* 1 | HASH JOIN | | 1834 | 245K| 333 (2)| 00:00:04 |
|* 2 | TABLE ACCESS FULL | T1 | 893 | 8037 | 5 (0)| 00:00:01 |
| 3 | VIEW | V_T | 2055 | 256K| 327 (2)| 00:00:04 |
| 4 | UNION-ALL | | | | | |
|* 5 | TABLE ACCESS FULL| T | 1067 | 97K| 164 (2)| 00:00:02 |
|* 6 | TABLE ACCESS FULL| T | 988 | 92872 | 164 (2)| 00:00:02 |
Predicate Information (identified by operation id):
1 - access("V_T"."OBJECT_ID"="T1"."OBJECT_ID")
2 - filter("T1"."OWNER"='SYS')
5 - filter("OBJECT_TYPE"='PACKAGE')
6 - filter("OBJECT_TYPE"='PACKAGE BODY')
21 rows selected.
SQL> explain plan for select /*+ push_pred(v_t) */ v_t.* from v_t, t1 where v_t.object_id = t1.object_id and t1.owner = 'SYS' ;
Explained.
SQL> select * from table(dbms_xplan.display) ;
PLAN_TABLE_OUTPUT
Plan hash value: 3926093524
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1834 | 263K| 292K (2)| 00:58:28 |
| 1 | NESTED LOOPS | | 1834 | 263K| 292K (2)| 00:58:28 |
|* 2 | TABLE ACCESS FULL | T1 | 893 | 11609 | 5 (0)| 00:00:01 |
| 3 | VIEW | V_T | 1 | 134 | 327 (2)| 00:00:04 |
| 4 | UNION ALL PUSHED PREDICATE | | | | | |
|* 5 | TABLE ACCESS FULL | T | 1 | 94 | 164 (2)| 00:00:02 |
|* 6 | TABLE ACCESS FULL | T | 1 | 94 | 164 (2)| 00:00:02 |
Predicate Information (identified by operation id):
2 - filter("T1"."OWNER"='SYS')
5 - filter("OBJECT_ID"="T1"."OBJECT_ID" AND "OBJECT_TYPE"='PACKAGE')
6 - filter("OBJECT_ID"="T1"."OBJECT_ID" AND "OBJECT_TYPE"='PACKAGE BODY')
20 rows selected.As you can see, the PUSH_PRED hint is ignored when I use the IN clause with subquery.
I believe this is same as what you are experiencing. However, when I change the sql to use
a JOIN (instead of subquery), the hint is not ignored.
p.s. I hope my JOIN query is semantically equivalent to IN..SUBQUERY. -
CBO bug? Lack of SORT UNIQUE.
Hi all,
Let's consider following case:
create table tmp as select rownum id, 0 sign from dual connect by level <= 100;
create index tmp_i on tmp(id,sign);
create table t as
select mod(rownum,2) id, mod(rownum,3) val
from dual
connect by level <= 100000;
begin
dbms_stats.gather_table_stats (
user,
'T',
estimate_percent => null,
method_opt => 'FOR ALL COLUMNS SIZE SKEWONLY',
cascade => true
end;
begin
dbms_stats.gather_table_stats (
user,
'TMP',
estimate_percent => null,
method_opt => 'FOR ALL COLUMNS SIZE SKEWONLY',
cascade => true
end;
/As you can see it scans TMP_I 50 000 times for statement with max (irrespective of distinct in subquery).
Is there any way to enforce CBO to make SORT UNIQUE for max as well as for count so that it scans TMP_I only 3 times?
SQL> select --+ leading(t) use_nl(t tmp)
2 max(id)
3 from tmp tmp
4 where tmp.sign = 0
5 and tmp.id in (select val from t where id = 1);
MAX(ID)
2
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.14 | 159 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.14 | 159 |
| 2 | NESTED LOOPS | | 1 | 49750 | 33333 |00:00:00.13 | 159 |
|* 3 | TABLE ACCESS FULL| T | 1 | 50000 | 50000 |00:00:00.02 | 156 |
|* 4 | INDEX RANGE SCAN | TMP_I | 50000 | 1 | 33333 |00:00:00.07 | 3 |
Predicate Information (identified by operation id):
3 - filter("ID"=1)
4 - access("TMP"."ID"="VAL" AND "TMP"."SIGN"=0)
SQL> select --+ leading(t) use_nl(t tmp)
2 max(id)
3 from tmp tmp
4 where tmp.sign = 0
5 and tmp.id in (select distinct val from t where id = 1);
MAX(ID)
2
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.14 | 159 |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.14 | 159 |
| 2 | NESTED LOOPS | | 1 | 49750 | 33333 |00:00:00.13 | 159 |
|* 3 | TABLE ACCESS FULL| T | 1 | 50000 | 50000 |00:00:00.01 | 156 |
|* 4 | INDEX RANGE SCAN | TMP_I | 50000 | 1 | 33333 |00:00:00.07 | 3 |
Predicate Information (identified by operation id):
3 - filter("ID"=1)
4 - access("TMP"."ID"="VAL" AND "TMP"."SIGN"=0)
SQL> select --+ leading(t) use_nl(t tmp)
2 count(id)
3 from tmp tmp
4 where tmp.sign = 0
5 and tmp.id in (select val from t where id = 1);
COUNT(ID)
2
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.03 | 158 | | | |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.03 | 158 | | | |
| 2 | NESTED LOOPS | | 1 | 3 | 2 |00:00:00.03 | 158 | | | |
| 3 | SORT UNIQUE | | 1 | 50000 | 3 |00:00:00.03 | 156 | 2048 | 2048 | 2048 (0)|
|* 4 | TABLE ACCESS FULL| T | 1 | 50000 | 50000 |00:00:00.01 | 156 | | | |
|* 5 | INDEX RANGE SCAN | TMP_I | 3 | 1 | 2 |00:00:00.01 | 2 | | | |
Predicate Information (identified by operation id):
4 - filter("ID"=1)
5 - access("TMP"."ID"="VAL" AND "TMP"."SIGN"=0)I can't figure out why SORT UNIQUE is absent for statement with max.
PS. 11gR2Thanks for reply.
user503699 wrote:
I don't think it is a good idea to compare your last and first query timings as they are 2 different queries.Ok. I could compare query with max(id), sign(count(*)+1) vs max(id), sign(1). They always produce the same results so can be considered as the same.
But I think that max(id), count(*) vs max(id) was enough to explain my point.
user503699 wrote:
If you are so sure of that you can write something like following :
SQL> with data as (select /*+ materialize */ distinct val as val from t where id = 1)
select max(id) from tmp tmp where tmp.sign = 0 and tmp.id in (select val from data) ; 2
I thought about that. I’m reluctant to use undocumented hints such as materialize. So folowing query has almost the best plan for my data:
with data as (select distinct val as val from t where id = 1 and rownum > 0)
select
max(id)
from tmp tmp
where tmp.sign = 0
and tmp.id in (select * from data);
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 8 | 50 (8)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 8 | | |
| 2 | NESTED LOOPS | | 3 | 24 | 50 (8)| 00:00:01 |
| 3 | VIEW | | 3 | 9 | 50 (8)| 00:00:01 |
| 4 | HASH UNIQUE | | 3 | 18 | 50 (8)| 00:00:01 |
| 5 | COUNT | | | | | |
|* 6 | FILTER | | | | | |
|* 7 | TABLE ACCESS FULL| T | 50000 | 292K| 47 (3)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | TMP_I | 1 | 5 | 0 (0)| 00:00:01 |
Predicate Information (identified by operation id):
6 - filter(ROWNUM>0)
7 - filter("ID"=1)
8 - access("TMP"."ID"="DATA"."VAL" AND "TMP"."SIGN"=0) And changing two hidden parameters may lead to the same plan as I expect:
alter session set "_gby_hash_aggregation_enabled" = false;
alter session set "_simple_view_merging" = false;
with data as (select distinct val as val from t where id = 1)
select
max(id)
from tmp tmp
where tmp.sign = 0
and tmp.id in (select * from data);
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 18 | 50 (8)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 18 | | |
| 2 | NESTED LOOPS | | 3 | 54 | 50 (8)| 00:00:01 |
| 3 | VIEW | | 3 | 39 | 50 (8)| 00:00:01 |
| 4 | SORT UNIQUE | | 3 | 18 | 50 (8)| 00:00:01 |
|* 5 | TABLE ACCESS FULL| T | 50000 | 292K| 47 (3)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | TMP_I | 1 | 5 | 0 (0)| 00:00:01 |
Predicate Information (identified by operation id):
5 - filter("ID"=1)
6 - access("TMP"."ID"="DATA"."VAL" AND "TMP"."SIGN"=0) But here I've got two additional questions:
1. no_use_hash_aggregation can be used instead of "alter session set "_gby_hash_aggregation_enabled" = false;"
What hint can be used instead of "alter session set "_simple_view_merging" = false;"?
2. Is there any way to enforce CBO to use for this one
select
max(id)
from tmp tmp
where tmp.sign = 0
and tmp.id in (select distinct val as val from t where id = 1 and rownum > 0);
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 5 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 5 | | |
| 2 | FIRST ROW | | 1 | 5 | 1 (0)| 00:00:01 |
|* 3 | INDEX FULL SCAN (MIN/MAX)| TMP_I | 1 | 5 | 1 (0)| 00:00:01 |
|* 4 | FILTER | | | | | |
| 5 | COUNT | | | | | |
|* 6 | FILTER | | | | | |
|* 7 | TABLE ACCESS FULL | T | 2 | 12 | 2 (0)| 00:00:01 |
------------------------------------------------------------------------------------- the same plan as for this
with data as (select distinct val as val from t where id = 1 and rownum > 0)
select
max(id)
from tmp tmp
where tmp.sign = 0
and tmp.id in (select * from data);
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 8 | 50 (8)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 8 | | |
| 2 | NESTED LOOPS | | 3 | 24 | 50 (8)| 00:00:01 |
| 3 | VIEW | | 3 | 9 | 50 (8)| 00:00:01 |
| 4 | HASH UNIQUE | | 3 | 18 | 50 (8)| 00:00:01 |
| 5 | COUNT | | | | | |
|* 6 | FILTER | | | | | |
|* 7 | TABLE ACCESS FULL| T | 50000 | 292K| 47 (3)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | TMP_I | 1 | 5 | 0 (0)| 00:00:01 |
I don't have anything against subquery factoring clause. Just for personal interest.
(I have read topic "Thread: Materialize a Subquery without using "with" clause"
Materialize a Subquery without using "with" clause
user503699 wrote:
If you are not going to change other things (like stats collection method, table/index structures etc.) which allow optimizer to choose better plan and you know your data better, you may need to be very specific with the hints and also may have to use additional hints in order to influence optimizer decisions.
One way to do that would be to get the base details from oracle as follows (and tweak them) :I didn't find keyword "ADVANCED" in specification for DISPLAY_CURSOR Function in documentation. Nice trick.
But anyway outline data makes sense only in case when query already has desirable execution plan. -
Accessing the records in ascending order using index hint
I am getting a problem in selecting the rows using the index hint which in i want to query in the ascending order.
for eg.
select /*+ index(temp_itr_header,tmp_itrhdr_1#IDX2) */ person_id
from temp_itr_headerOracle knows that it doesn't have to resort data if it accessed individual rows from an index with the same sort order. In other words, adding the ORDER BY clause need not cause Oracle to do any extra work. Adding the ORDER BY does guarantee that the results will be sorted and will tend to cause Oracle to use the index, assuming it can avoid the sort that way. If you don't have an ORDER BY, Oracle is free to return rows in whatever order it would like regardless of your hint.
If you want to use the index hint, the syntax is
SELECT /*+ INDEX(temp_itr_header <<index name>>) */Note that you do not want to have a comma in your hint. Of course, if your statistics are accurate, you shouldn't need to resort to a hint here-- the CBO should pick the most efficient path.
Justin
Distributed Database Consulting, Inc.
http://www.ddbcinc.com/askDDBC -
CBO not picking the right execution plan
Database: Oracle 9.2.0.6 EE
OS:Solaris 9
I am trying to tune a query that is generated via Siebel Analytics. I am seeing a behaviour which is puzzling me but hopefully would be 'elementary' for someone like JPL.
The query is based on a total of 7 tables. If I comment out any 2 dimension tables, the query picks up the right index on the fact table. However, the moment I add another table to the query, the plan goes awry.
The query with 5 tables is as below:
select count(distinct decode( T30256.HEADER_FLG , 'N' , T30256.ROW_WID ) ) as c1,
T352305.DAY_DT as c2,
case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end as c3,
T352305.ROW_WID as c5
from
W_PRODUCT_D T30955,
W_PRDATTRNM_D T44643,
W_DAY_D T352305,
W_ORDERITEM_F T30256,
W_PRDATTR_D T40081
where ( T30955.ROW_WID = T44643.ROW_WID
and T30256.LAST_UPD_DT_WID = T352305.ROW_WID
and T30256.PROD_ATTRIB_WID = T40081.ROW_WID
and T30256.PROD_WID = T30955.ROW_WID
and T30955.PROD_NAME = 'Mobile Subscription'
and (case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end in ('BT150BB-18M', 'BT250BB-18M', 'BT50BB-18M', 'BT600BB-18M'))
and T352305.DAY_DT between TO_DATE('2008-09-27' , 'YYYY-MM-DD') - 7 and TO_DATE('2008-09-27' , 'YYYY-MM-DD') - 1
group by
T352305.ROW_WID, T352305.DAY_DT,
case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end
;And the execution plan is as below:
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
| 0 | SELECT STATEMENT | | 269 | 25824 | 18660 (3)|
| 1 | SORT GROUP BY | | 269 | 25824 | 18660 (3)|
| 2 | NESTED LOOPS | | 269 | 25824 | 18658 (3)|
| 3 | NESTED LOOPS | | 6826 | 579K| 4734 (3)|
| 4 | MERGE JOIN CARTESIAN | | 8 | 544 | 6 (17)|
| 5 | NESTED LOOPS | | 1 | 54 | 4 (25)|
| 6 | TABLE ACCESS BY INDEX ROWID| W_PRODUCT_D | 1 | 37 | 3 (34)|
|* 7 | INDEX RANGE SCAN | W_PRODUCT_D_M2 | 1 | | 2 (50)|
| 8 | TABLE ACCESS BY INDEX ROWID| W_PRDATTRNM_D | 1 | 17 | 2 (50)|
|* 9 | INDEX UNIQUE SCAN | W_PRDATTRNM_D_P1 | 1 | | |
| 10 | BUFFER SORT | | 8 | 112 | 4 (0)|
| 11 | TABLE ACCESS BY INDEX ROWID| W_DAY_D | 8 | 112 | 3 (34)|
|* 12 | INDEX RANGE SCAN | W_DAY_D_M39 | 8 | | 2 (50)|
| 13 | TABLE ACCESS BY INDEX ROWID | W_ORDERITEM_F | 849 | 16131 | 592 (3)|
|* 14 | INDEX RANGE SCAN | W_ORDERITEM_F_INDX9 | 852 | | 4 (25)|
|* 15 | INDEX RANGE SCAN | W_PRDATTR_D_M29_T1 | 1 | 9 | 3 (34)|
----------------------------------------------------------------------------------------------Note how the dimension tables W_PRODUCT_D & W_DAY_D are joined using cartesian join before joining to the fact table W_ORDERITEM_F using the composite index 'W_ORDERITEM_F_INDX9'. This index consists of LAST_UPD_DT_WID, PROD_WID and ACTION_TYPE_WID, which are foreign keys to the dimension tables.
Now if I add one more table to the query:
select count(distinct decode( T30256.HEADER_FLG , 'N' , T30256.ROW_WID ) ) as c1,
T352305.DAY_DT as c2,
case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end as c3,
T30371.X_BT_DLR_GROUP as c4,
T352305.ROW_WID as c5
from W_PRODUCT_D T30955,
W_PRDATTRNM_D T44643,
W_DAY_D T352305,
W_ORDERITEM_F T30256,
W_ORDER_D T30371,
W_PRDATTR_D T40081
where ( T30955.ROW_WID = T44643.ROW_WID
and T30256.LAST_UPD_DT_WID = T352305.ROW_WID
and T30256.PROD_ATTRIB_WID = T40081.ROW_WID
and T30256.PROD_WID = T30955.ROW_WID
and T30256.ORDER_WID = T30371.ROW_WID
and T30955.PROD_NAME = 'Mobile Subscription'
and T30371.STATUS_CD = 'Complete'
and T30371.ORDER_TYPE = 'Sales Order'
and (case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end in ('BT150BB-18M', 'BT250BB-18M', 'BT50BB-18M', 'BT600BB-18M'))
and T352305.DAY_DT between TO_DATE('2008-09-27' , 'YYYY-MM-DD') - 7 and TO_DATE('2008-09-27' , 'YYYY-MM-DD') - 1
group by T30371.X_BT_DLR_GROUP, T352305.ROW_WID, T352305.DAY_DT,
case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end;I have added a single table W_ORDER_D to the query, and the execution plan is:
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
| 0 | SELECT STATEMENT | | 44 | 6336 | 78695 (3)|
| 1 | SORT GROUP BY | | 44 | 6336 | 78695 (3)|
| 2 | NESTED LOOPS | | 44 | 6336 | 78694 (3)|
| 3 | NESTED LOOPS | | 269 | 27707 | 78145 (3)|
|* 4 | HASH JOIN | | 6826 | 626K| 64221 (3)|
| 5 | TABLE ACCESS BY INDEX ROWID | W_DAY_D | 8 | 112 | 4 (25)|
|* 6 | INDEX RANGE SCAN | W_DAY_D_M39 | 1 | | 3 (34)|
| 7 | TABLE ACCESS BY INDEX ROWID | W_ORDERITEM_F | 86886 | 2206K| 64197 (3)|
| 8 | NESTED LOOPS | | 87004 | 6797K| 64200 (3)|
| 9 | NESTED LOOPS | | 1 | 54 | 4 (25)|
| 10 | TABLE ACCESS BY INDEX ROWID| W_PRODUCT_D | 1 | 37 | 3 (34)|
|* 11 | INDEX RANGE SCAN | W_PRODUCT_D_M2 | 1 | | 2 (50)|
| 12 | TABLE ACCESS BY INDEX ROWID| W_PRDATTRNM_D | 1 | 17 | 2 (50)|
|* 13 | INDEX UNIQUE SCAN | W_PRDATTRNM_D_P1 | 1 | | |
|* 14 | INDEX RANGE SCAN | W_ORDERITEM_F_N6 | 86886 | | 212 (18)|
|* 15 | INDEX RANGE SCAN | W_PRDATTR_D_M29_T1 | 1 | 9 | 3 (34)|
|* 16 | INDEX RANGE SCAN | W_ORDER_D_N6 | 1 | 41 | 3 (34)|
-----------------------------------------------------------------------------------------------Now CBO doesn't choose the composite index and the cost also has increased to 78695. But if I simply add an /*+ORDERED*/ hint to the above query, so that it should join the dimension tables before joining to fact table, then the cost drops to 20913. This means that CBO is not choosing the plan with the lowest cost. I tried increasing the optimizer_max_permutations to 80000, setting session level optimizer_dynamic_sampling to 8 (just to see if it works), but no success.
Could you please advise how to overcome this problem?
Many thanks.joshic wrote:
Database: Oracle 9.2.0.6 EE
OS:Solaris 9
I am trying to tune a query that is generated via Siebel Analytics. I am seeing a behaviour which is puzzling me but hopefully would be 'elementary' for someone like JPL.
The query is based on a total of 7 tables. If I comment out any 2 dimension tables, the query picks up the right index on the fact table. However, the moment I add another table to the query, the plan goes awry.
I have added a single table W_ORDER_D to the query, and the execution plan is:
Now CBO doesn't choose the composite index and the cost also has increased to 78695. But if I simply add an /*+ORDERED*/ hint to the above query, so that it should join the dimension tables before joining to fact table, then the cost drops to 20913. This means that CBO is not choosing the plan with the lowest cost. I tried increasing the optimizer_max_permutations to 80000, setting session level optimizer_dynamic_sampling to 8 (just to see if it works), but no success.Back to the original question:
* Can you force the index usage of the composite index on W_ORDERITEM_F in the second query using an INDEX hint (instead of the ORDERED hint)? If yes, what does the plan look like, particularly what cost is reported?
* Could you post the plans including the "Predicate Information" section below the plan output?
* What is the definition of the index W_ORDERITEM_F_N6 on W_ORDERITEM_F?
* Are the cardinalities reported in the execution plans close to reality or way off? The best way to verify this would be to run your query with SQL tracing enabled and generate a tkprof output. If you do so please post the tkprof output here as well.
Regards,
Randolf
Oracle related stuff blog:
http://oracle-randolf.blogspot.com/
SQLTools++ for Oracle (Open source Oracle GUI for Windows):
http://www.sqltools-plusplus.org:7676/
http://sourceforge.net/projects/sqlt-pp/ -
CBO not picking correct indexes or doing Full Scans
Database version - 10.2.0.4
OS : Solaris 5.8
Storage: SAN
Application : PeopleSoft Financials
DB size : 450 gb
DB server : 12 CPU ( 900 Mghz each ), 36 GB RAM
ASMM - sga_target_size = 5 gb
Locally managed tablespaces - MANUAL
- db_file_multiblock_read_count - not set explicitly in spfile ( implicitly defaulted to 128 )
- other optimizer related parameters are set to default
- system_statistics - CPUSPEEDNW=456.282722513089, IOSEEKTIM =10, IOTFRSPEED=4096
- dictionary object system stats were last gather in Nov 09
- stats on schema objs are gathered every night using custom script, but I have to say there are some histograms on some tables which were gathered by PS admins
begin
dbms_stats.gather_schema_stats(
ownname=> 'SCHEMANM' ,
cascade=> DBMS_STATS.AUTO_CASCADE,
estimate_percent=> DBMS_STATS.AUTO_SAMPLE_SIZE,
degree=> 10,
no_invalidate=> DBMS_STATS.AUTO_INVALIDATE,
granularity=> 'AUTO',
method_opt=> 'FOR ALL COLUMNS SIZE 1',
options=> 'GATHER STALE');
end;
Details :
We are experiencing erratic database performance. It was upgraded from 9i to 10g along with PS software upgrade. A job that runs on one day in 12 hrs(that is high in itself) would take more than 24 hrs another day.
We have Test and Staging envs on other servers which do not have performance issues of production magnitude. The test, staging dbs are clones of prod, created by ftp'ing files from one location to another, not sure if that makes any difference but just pointing out.
On Prod db some symptoms which baffle me are :
1) sql executing for over 40 minutes, check the plan and it is using an "incorrect" index, idx1. Hint sql with "correct" index, idx2, and it runs in few seconds. result set <400 rows. Cost with hint with idx2 is HIGHER. This scenario is still understandable as CBO is likely to pick a lower cost.
- But why is there so much discrepancy in cost and response time?
- what is influencing the CBO to pick an index which is obviously not the fastest response time?
2) sql plan shows FTS , execution time , runs forever . But a hint with index in this case shows the cost is LOWER and response time is in seconds, so why is CBO not even evaluating this index path? Because as I understand the CBO, it will always pick the lower cost plan.
What is influencing the CBO. Is it system stats? or any other database parameter? or the hardware ? the large SGA?? histogram ?? Stats gathering?? is there is any known issue with DBMS_STATS.AUTO_SAMPLE_SIZE and cardinaility?
Where should I put my focus?
What am I looking for ?
I do have Jonathan Lewis's Cost-Based Oracle Fundamentals which I have just started so perhaps I will be enlightened but while I read it, it would be of immense help to get some advice from forum gurus.
At this time I am not posting any exec plan or code, but I can do so if required.
Thanks,
JCPicking up just a couple of points - the ones about: why can performance change so much from one day to the next, and how can the optimizer miss a plan which (when hinted) shows a lower cost.
NOTE: These are only suggestions that may allow you to look at critical code and recognise the pattern of behaviour
Performance change:
You are using "gather stale" which means tables won't get new statistics is the volume of change since the last collection is "small". But peoplesoft tables can be big, so some tables may need a lot of data to change (which might take several days or weeks) before they get new stats. The 10g optimizer is able to compare the high-values stored with the predicate values supplied in queries, and decide that since the predicate is higher than the known highest value, it should scale down its estimates of data returned. After a few days when the stats haven't changed, the optimizer suddenly gets to the point where the estimated data size is much too small and the plan changes to something that you can see is bad. The typical example here is a date column that increases with time, and a query on "recent data" - the optimizer basically says: "you want 9th Feb - the high value says 28th Jan, there's virtually no data". Keeping date and sequence-based high-values up to date is important. (See dbms_stats.set_column_stats).
Lower cost plans available:
Sometimes this is a bug. Sometimes it's an unlucky side effect. When a query has a large number of tables in a single query block, the optimizer can only examine a limited subset of the possible join orders (for example, a 10-table join has over 3million join orders, and the default limit is 2,000 checked). Because of this limit, Oracle starts working through a few joins orders in a methodical manner, then does a "big jump" to a different set of join orders where it checks a few more join orders, then does another "big jump". It may be that it never gets to your nice plan with the lower cost because it doesn't have time. However, by putting in the hint, you may make some of the orders the optimizer would have examined drop out of the "methodical list" - so it examines a few more in one sequence, or possibly "jumps" to a different sequence that it would not othewise have reached. (I wrote a related blog item some time ago that might make this clearer, even though it's not about exactly the same phenomenon: http://jonathanlewis.wordpress.com/2006/11/03/table-order/ ).
Your recent comment about dynamic sampling is correct, by the way - by default (which for your version is is level 2 as a system parameter) it will only apply for tables which don't have any statistics. It's only for higher levels, or when explicitly hinted for tables, that it can apply to tables with existing statistics.
Regards
Jonathan Lewis
http://jonathanlewis.wordpress.com
http://www.jlcomp.demon.co.uk
"Science is more than a body of knowledge; it is a way of thinking"
Carl Sagan
To post code, statspack/AWR report, execution plans or trace files, start and end the section with the tag {noformat}{noformat} (lowercase, curly brackets, no spaces) so that the text appears in fixed format. -
Degree in parallel hint is not working properly.
Hi Experts,
I am using the following delete statement.
delete /*+ parallel(t,30) */ from master_header t;
Sometimes the degree 30 is not working.
On what basis we have to give the degree.
Is the degree is session specific?
To use this hint we need to follow any guidelines or check list.
Please help me.
Thanks.does your system have the capacity to have multiple parallel queries with 30 processes running concurrently? (like a Sun 6900 with 48-dual core processors or something. A little 8cpu box will not be able to handle this. That is why it is called a "hint". While you can "hint" in a query, the CBO can (and in this case will) ignore it and will reduce the parallel degree to what it sees the system can handle. It tries to keep developers from doing stupid stuff.
-
Why CBO don't use function-base index when I use like and bind variable
Hello
I have litle problem with function-base index and like with bind variable.
When I use like with bind variable, the CBO don't use my function-base index.
For example when I create table and index:
ALTER SESSION SET NLS_SORT='BINARY_CI';
ALTER SESSION SET NLS_COMP='LINGUISTIC';
alter session set nls_language='ENGLISH';
-- DROP TABLE TEST1;
CREATE TABLE TEST1 (K1 VARCHAR2(32));
create index test1_idx on test1(nlssort(K1,'nls_sort=BINARY_CI'));
INSERT INTO TEST1
SELECT OBJECT_NAME FROM ALL_OBJECTS;
COMMIT;
When I run:
ALTER SESSION SET NLS_SORT='BINARY_CI';
ALTER SESSION SET NLS_COMP='LINGUISTIC';
SELECT * FROM TEST1 WHERE K1 = 'abcd';
or
SELECT * FROM TEST1 WHERE K1 LIKE 'abcd%';
CBO use index.
PLAN_TABLE_OUTPUT
SQL_ID 4vrmp7cshbvqy, child number 1
SELECT * FROM TEST1 WHERE K1 LIKE 'abcd%'
Plan hash value: 1885706448
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | | | 1 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST1 | 2 | 98 | 1 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | TEST1_IDX | 2 | | 1 (0)| 00:00:01 |
Predicate Information (identified by operation id):
2 - access("TEST1"."SYS_NC00002$">=HEXTORAW('6162636400') AND
"TEST1"."SYS_NC00002$"<HEXTORAW('6162636500') )
but when I run
SELECT * FROM TEST1 WHERE K1 LIKE :1;
CBO don't use index
PLAN_TABLE_OUTPUT
SQL_ID 9t461s1669gru, child number 0
SELECT * FROM TEST1 WHERE K1 LIKE :1
Plan hash value: 4122059633
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | | | 89 (100)| |
|* 1 | TABLE ACCESS FULL| TEST1 | 2 | 48 | 89 (3)| 00:00:02 |
Predicate Information (identified by operation id):
1 - filter("K1" LIKE :1)
What should I change to force CBO to use index.
I don't wont use index hint in query.
My oracle version:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for 64-bit Windows: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - ProductionOK. But why if I create normal index (create index test1_idx on test1(K1)) and return to default nls settings this same query use index.
PLAN_TABLE_OUTPUT
SQL_ID 9t461s1669gru, child number 0
SELECT * FROM TEST1 WHERE K1 LIKE :1
Plan hash value: 598212486
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | | | 1 (100)| |
|* 1 | INDEX RANGE SCAN| TEST1_IDX | 1 | 18 | 1 (0)| 00:00:01 |
Predicate Information (identified by operation id):
1 - access("K1" LIKE :1)
filter("K1" LIKE :1)
Note
- dynamic sampling used for this statement (level=2)
when index is function-base (create index test1_idx on test1(nlssort(K1,'nls_sort=BINARY_CI')))
PLAN_TABLE_OUTPUT
SQL_ID 9t461s1669gru, child number 1
SELECT * FROM TEST1 WHERE K1 LIKE :1
Plan hash value: 4122059633
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | | | 89 (100)| |
|* 1 | TABLE ACCESS FULL| TEST1 | 3 | 54 | 89 (3)| 00:00:02 |
Predicate Information (identified by operation id):
1 - filter("K1" LIKE :1)
Note
- dynamic sampling used for this statement (level=2)
when I create index with upper function "index test1_idx on test1(upper(K1))" the query use index
SELECT * FROM TEST1 WHERE upper(K1) LIKE :1
Plan hash value: 1885706448
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | | | 1 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST1 | 4481 | 157K| 1 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | TEST1_IDX | 806 | | 1 (0)| 00:00:01 |
Predicate Information (identified by operation id):
2 - access("TEST1"."SYS_NC00002$" LIKE :1)
filter("TEST1"."SYS_NC00002$" LIKE :1)
Note
- dynamic sampling used for this statement (level=2) -
Hi guys,
I'm runing some tests to check weather processed_flag should have NULL for already processed values or not. I thought that having a smaller index on the processed_flag column should be better.
Check the following example:
CREATE TABLE processed_flag_not_null AS
SELECT LEVEL id,
lpad('A', 10, 'A') VALUE,
CASE
WHEN MOD(LEVEL, 100) = 0 THEN
0
ELSE
1
END processed_flag
FROM dual
CONNECT BY LEVEL <= 1000000;
CREATE INDEX IDX_PROCESSED_FLAG_NOT_NULL ON PROCESSED_FLAG_NOT_NULL (PROCESSED_FLAG);
CREATE TABLE processed_flag_null AS
SELECT LEVEL id,
lpad('A', 10, 'A') VALUE,
CASE
WHEN MOD(LEVEL, 100) = 0 THEN
0
END processed_flag
FROM dual
CONNECT BY LEVEL <= 1000000;
CREATE INDEX IDX_PROCESSED_FLAG_NULL ON PROCESSED_FLAG_NULL (PROCESSED_FLAG);
BEGIN
dbms_stats.gather_table_stats(USER, 'PROCESSED_FLAG_NOT_NULL', cascade => TRUE);
dbms_stats.gather_table_stats(USER, 'PROCESSED_FLAG_NULL', cascade => TRUE);
END;The first strange thing I've found was in all_tab_histograms:
SELECT *
FROM all_tab_histograms
WHERE table_name IN ('PROCESSED_FLAG_NOT_NULL', 'PROCESSED_FLAG_NULL')
AND column_name = 'PROCESSED_FLAG'
ORDER BY 2,4;
TABLE_NAME COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE
PROCESSED_FLAG_NOT_NULL PROCESSED_FLAG 0 0
PROCESSED_FLAG_NOT_NULL PROCESSED_FLAG 1 1
PROCESSED_FLAG_NULL PROCESSED_FLAG 0 0
PROCESSED_FLAG_NULL PROCESSED_FLAG 1 0Only after running queries against both tables with ("where processed_flag = 0") and gathering table stats again I get the following histograms:
TABLE_NAME COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE
PROCESSED_FLAG_NOT_NULL PROCESSED_FLAG 59 0
PROCESSED_FLAG_NOT_NULL PROCESSED_FLAG 5598 1
PROCESSED_FLAG_NULL PROCESSED_FLAG 10000 0Can someone explain to me this behaviour?
Now for the main question, I don't understand why the CBO chooses an Index Range Scan for the PROCESSED_FLAG_NOT_NULL table, and a Full Table Scan for the PROCESSED_FLAG_NULL:
SQL> set timing on
SQL> set autotrace traceonly
SQL> SELECT *
2 FROM processed_flag_not_null
3 WHERE processed_flag = 0;
10000 rows selected.
Elapsed: 00:00:00.20
Execution Plan
Plan hash value: 3652560023
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 10539 | 195K| 93 (0)| 00:00:02 |
| 1 | TABLE ACCESS BY INDEX ROWID| PROCESSED_FLAG_NOT_NULL | 10539 | 195K| 93 (0)| 00:00:02 |
|* 2 | INDEX RANGE SCAN | IDX_PROCESSED_FLAG_NOT_NULL | 10539 | | 23 (0)| 00:00:01 |
Predicate Information (identified by operation id):
2 - access("PROCESSED_FLAG"=0)
Statistics
1 recursive calls
0 db block gets
4444 consistent gets
0 physical reads
0 redo size
306954 bytes sent via SQL*Net to client
7745 bytes received via SQL*Net from client
668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10000 rows processed
SQL> SELECT *
2 FROM processed_flag_null
3 WHERE processed_flag = 0;
10000 rows selected.
Elapsed: 00:00:00.22
Execution Plan
Plan hash value: 1150676937
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 10000 | 166K| 802 (2)| 00:00:10 |
|* 1 | TABLE ACCESS FULL| PROCESSED_FLAG_NULL | 10000 | 166K| 802 (2)| 00:00:10 |
Predicate Information (identified by operation id):
1 - filter("PROCESSED_FLAG"=0)
Statistics
1 recursive calls
0 db block gets
3571 consistent gets
0 physical reads
0 redo size
174974 bytes sent via SQL*Net to client
7745 bytes received via SQL*Net from client
668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10000 rows processedIf I compare both aproaches using Runstats:
BEGIN
runstats_pkg.rs_start;
BEGIN
FOR i IN 1 .. 1000
LOOP
FOR x IN (SELECT *
FROM processed_flag_not_null
WHERE processed_flag = 0)
LOOP
NULL;
END LOOP;
END LOOP;
END;
runstats_pkg.rs_middle;
BEGIN
FOR i IN 1 .. 1000
LOOP
FOR x IN (SELECT *
FROM processed_flag_null
WHERE processed_flag = 0)
LOOP
NULL;
END LOOP;
END LOOP;
END;
runstats_pkg.rs_stop;
END;
-- Output
Run1 ran in 4295 hsecs
Run2 ran in 5123 hsecs
run 1 ran in 83.84% of the timeIf I compare the processed_flag_null without hints versus processed_flag_null with index hint this are the results:
BEGIN
runstats_pkg.rs_start;
BEGIN
FOR i IN 1 .. 1000
LOOP
FOR x IN (SELECT *
FROM processed_flag_null
WHERE processed_flag = 0)
LOOP
NULL;
END LOOP;
END LOOP;
END;
runstats_pkg.rs_middle;
BEGIN
FOR i IN 1 .. 1000
LOOP
FOR x IN (SELECT /*+ index(processed_flag_null IDX_PROCESSED_FLAG_NULL) */
FROM processed_flag_null
WHERE processed_flag = 0)
LOOP
NULL;
END LOOP;
END LOOP;
END;
runstats_pkg.rs_stop;
END;
-- Output
Run1 ran in 5017 hsecs
Run2 ran in 2212 hsecs
run 1 ran in 226.81% of the timeAs I expected using the hint is more than twices fast of not using the index, why doesn't the CBO choose the index aproach? Can I tune the stats to give the CBO more information?
Thanks in advance,
Manuel Vidigal
EDIT:
Forgot to mention the tests were done using my laptop with Oracle 11.2.0.1.0.
Edited by: Manuel Vidigal on 16/Set/2010 11:53I don't have runstats installed on my 11.2 instance so i can't reproduce your findings there, however with a simple SET TIMING ON and observing the results i get no noticeable difference between the INDEX hinted query and the not hinted query (i have an isolated sandbox where i am on the only user on the server so this is a reasonably safe method in my opinion).
TUBBY_TUBBZ?
BEGIN
FOR i IN 1 .. 1000
LOOP
FOR x IN (SELECT *
FROM processed_flag_null
WHERE processed_flag = 0)
LOOP
NULL;
END LOOP;
END LOOP;
END;
12 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:51.26
BEGIN
FOR i IN 1 .. 1000
LOOP
FOR x IN (SELECT /*+ index(processed_flag_null IDX_PROCESSED_FLAG_NULL) */
FROM processed_flag_null
WHERE processed_flag = 0)
LOOP
NULL;
END LOOP;
END LOOP;
END;
13 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:50.53The results you are seeing are a consequence of the tiny nature of your table. The way you have set it up you should be seeing roughly 300 rows fitting on to a single data block (i observed 325 on my instance running on ASM) and you are only interested in returning every 100th row (again the way you set up your data) but to get every 100th row means you will have to visit EVERY block in the table.
select
blocks
from dba_segments
where segment_name = 'PROCESSED_FLAG_NULL';
PL/SQL procedure successfully completed.
Elapsed: 00:00:04.51
TUBBY_TUBBZ?
BLOCKS
3072
1 row selected.
Elapsed: 00:00:00.13Since you haven't ordered your data it's going to be inserted as the rows are generated from DUAL (so ordered by LEVEL asc). That means that you will need to get every block from the table (getting roughly 3 results per block).
In order to visit every block in the table via an index access you cannot utilize multi-block IO, so that would be the reason the optimizer took the FULL table scan as it's choice.
So in terms of 'fixing' this you have a couple of options.
1) change the order of the data so it's not so evenly distributed across the blocks in the table (or possibly create this table as an index organized one so the values are sorted upon insertion and kept physically close to each other)
2) change the size of a row such that less rows will fit on a single data block meaning that you will not actually have to return every data block for this table via your query
lpad('A', 1000, 'A') VALUE,Would be a sufficient change to shift things in favor of index access in your example.
Maybe you are looking for
-
Mac G5i no longer recognising digital camera
This was working fine one day and then not the other, Previously whenever I plugged in my Pentax Optio S5i via USB port it was picked up immediately and I could copy over photos. Now nothing happens I go to System Profiler and under USB it's there, s
-
Can we use free goods in MTO,thirdparty
Hi, We cannot use free goods in MTO,thirdparty processing, do we have any sap documents supporting it,can u pls provide. Regards SR
-
Can I use a US-122MKII with Logic Pro x?
Hi All! This is my first time on here so nice to meet you My question is: Can I use the TASCAM US-122MKII with the new Logic Pro x on an iMac? I've been using the TASCAM with my old Powerbook running Logic Pro 9 and it worked fine. Now however I can'
-
How can I turn a recorded voice into multiple voices that sound like a group of singers?
I'm using Logic Pro 9. Could someone teach me how to turn a single recorded voice into multiple voices that sound like a group singing together by mixing or other techniques ? I don't have the luxury of a group singing together with me when I do reco
-
No recovery option is showing up
hi im having problems with my toshiba satellite L745 and it wont load my windows 7..it always stops on black screen with flashing cursor..i keep trying the recovery option thing but no hdd recovery option is showing up..please i need some advice