Reading an execution plan puzzle
Date: Sun, 17 Feb 2008 16:14:56 +0100
Message-ID: <001101c87177$daf62330$6401a8c0@trivadis.com>
Hello Listers,
Recently I have got puzzled when I tried to read the following execution plan . The SQL was:
explain plan for
SELECT /*+ leading(A C_at_subq1) index(A SUPPLIER_SCHEDULE_UK) opt_param('_or_expand_nvl_predicate', 'FALSE') */ OBJID,
OBJVERSION
FROM IFSAPP.SUPPLIER_SCHEDULE A
WHERE VENDOR_NO = NVL(:B5 ,VENDOR_NO) AND
CONTRACT = NVL(:B4 , CONTRACT) AND PART_NO = NVL(:B3 , PART_NO) AND NVL(AGREEMENT_ID, '@') = NVL(:B2 , NVL(AGREEMENT_ID, '@')) AND SUPP_SCHEDULE_TYPE_DB = NVL(:B1 , SUPP_SCHEDULE_TYPE_DB) AND ---OBJSTATE IN ('Created', 'Out Of Tolerance', 'Tol Check In Process') OBJSTATE IN ('Superceded') AND CONTRACT IN (SELECT IFSAPP.USER_ALLOWED_SITE_API.AUTHORIZED(CONTRACT) FROM IFSAPP.USER_ALLOWED_SITE ) AND EXISTS (SELECT 1 FROM IFSAPP.SUPP_SCHED_AGREEMENT_PART B WHERE A.VENDOR_NO=B.VENDOR_NO AND A.CONTRACT=B.CONTRACT AND A.AGREEMENT_ID=B.AGREEMENT_ID AND A.PART_NO=B.PART_NO AND NVL(:B6 , B.APPROVER_ID) = B.APPROVER_ID ) AND A.SCHEDULE_NO = ( select /*+ qb_name(subq1) */ MAX(C.SCHEDULE_NO) FROM IFSAPP.SUPPLIER_SCHEDULE C where C.VENDOR_NO = A.VENDOR_NO AND C.CONTRACT = A.CONTRACT AND C.PART_NO = A.PART_NO AND C.SUPP_SCHEDULE_TYPE_DB = A.SUPP_SCHEDULE_TYPE_DB and rownum =1 )
ORDER BY
VENDOR_NO,
CONTRACT,
PART_NO,
SUPP_SCHEDULE_TYPE_DB DESC,
SCHEDULE_NO ; Output of "explain plan for" was:
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 110 | 609 (5)| 00:00:06 |
| 1 | SORT ORDER BY | | 1 | 110 | 609 (5)| 00:00:06 |
| 2 | NESTED LOOPS SEMI | | 1 | 110 | 603 (5)| 00:00:06 |
|* 3 | TABLE ACCESS BY INDEX ROWID | SUPPLIER_SCHEDULE_TAB | 1 | 82 | 602 (5)| 00:00:06 | |* 4 | INDEX FULL SCAN | SUPPLIER_SCHEDULE_UK | 1 | | 601 (5)| 00:00:06 | |* 5 | INDEX FAST FULL SCAN | USER_ALLOWED_SITE_B_IX | 2 | 12 | 2 (0)| 00:00:01 |
| 6 | SORT AGGREGATE | | 1 | 39 | | |
| 7 | FIRST ROW | | 1 | 39 | 3 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN (MIN/MAX)| SUPPLIER_SCHEDULE_UK | 1 | 39 | 3 (0)| 00:00:01 | |* 9 | TABLE ACCESS BY INDEX ROWID | SUPP_SCHED_AGREEMENT_PART_TAB | 1 | 28 | 1 (0)| 00:00:01 | |* 10 | INDEX UNIQUE SCAN | SUPP_SCHED_AGREEMENT_PART_PK | 1 | | 0 (0)| 00:00:01 | -------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
3 - filter("ROWSTATE"='Superceded' AND NVL("AGREEMENT_ID",'@')=NVL(:B2,NVL("AGREEMENT_ID",'@'))) 4 - filter("PART_NO"=NVL(:B3,"PART_NO") AND "SUPP_SCHEDULE_TYPE"=NVL(:B1,"SUPP_SCHEDULE_TYPE") AND "CONTRACT"=NVL(:B4,"CONTRACT") AND "VENDOR_NO"=NVL(:B5,"VENDOR_NO") AND EXISTS (SELECT /*+ */ 0 FROM IFSAPP."USER_ALLOWED_SITE_TAB" "USER_ALLOWED_SITE_TAB" WHERE "USER_ALLOWED_SITE_API"."AUTHORIZED"("CONTRACT")=:B1) AND "SCHEDULE_NO"= (SELECT /*+ QB_NAME ("SUBQ1")*/ MAX("SCHEDULE_NO") FROM IFSAPP."SUPPLIER_SCHEDULE_TAB" "SUPPLIER_SCHEDULE_TAB" WHERE "SUPP_SCHEDULE_TYPE"=:B2 AND "VENDOR_NO"=:B3 AND "CONTRACT"=:B4 AND "PART_NO"=:B5)) 5 - filter("USER_ALLOWED_SITE_API"."AUTHORIZED"("CONTRACT")=:B1) 8 - access("PART_NO"=:B1 AND "CONTRACT"=:B2 AND "VENDOR_NO"=:B3 AND "SUPP_SCHEDULE_TYPE"=:B4) 9 - filter("APPROVER_ID"=NVL(:B6,"APPROVER_ID")) 10 - access("AGREEMENT_ID"="AGREEMENT_ID" AND "VENDOR_NO"="VENDOR_NO" AND "CONTRACT"="CONTRACT" AND "PART_NO"="PART_NO")
I could not interpret the right sequence of steps 4,5 and 6. After tracing the SQL with event 10046 I have got the following execution plan (from the trace file):
Rows Row Source Operation
------- --------------------------------------------------- 0 SORT ORDER BY (cr=19954 pr=0 pw=0 time=343900 us) 0 NESTED LOOPS SEMI (cr=19954 pr=0 pw=0 time=343866 us) 0 TABLE ACCESS BY INDEX ROWID SUPPLIER_SCHEDULE_TAB (cr=19954 pr=0 pw=0 time=343841 us) 5022 INDEX FULL SCAN SUPPLIER_SCHEDULE_UK (cr=15701 pr=0 pw=0 time=322874 us)(object id 198167) 1 INDEX FAST FULL SCAN USER_ALLOWED_SITE_B_IX (cr=12 pr=0 pw=0 time=1368 us)(object id 9690) 5022 SORT AGGREGATE (cr=15066 pr=0 pw=0 time=112146 us) 5022 COUNT STOPKEY (cr=15066 pr=0 pw=0 time=93179 us) 5022 FIRST ROW (cr=15066 pr=0 pw=0 time=75120 us) 5022 INDEX RANGE SCAN (MIN/MAX) SUPPLIER_SCHEDULE_UK (cr=15066 pr=0 pw=0 time=62607 us)(object id 198167) 0 INDEX RANGE SCAN SUPP_SCHED_AGREEMENT_PART_UK (cr=0 pr=0 pw=0 time=0 us)(object id 198165)
Clearly, here the first step was step 4 (INDEX FULL SCAN SUPPLIER_SCHEDULE_UK ), which is the Parent step of INDEX FAST FULL SCAN USER_ALLOWED_SITE_B_IX and SORT AGGREGATE steps .
Afterwards I have changed slightly the sql statement , using the full/aliased object notation of CONTACT column
Of IFSAPP.SUPPLIER_SCHEDULE A table (here I am providing only the changed lines:
"CONTRACT IN" -> "A.CONTRACT IN"
"IFSAPP.USER_ALLOWED_SITE_API.AUTHORIZED(CONTRACT)" -> "IFSAPP.USER_ALLOWED_SITE_API.AUTHORIZED(A.CONTRACT)"):
...
A.CONTRACT IN (SELECT /*+ qb_name(subq2) */ IFSAPP.USER_ALLOWED_SITE_API.AUTHORIZED(A.CONTRACT) FROM IFSAPP.USER_ALLOWED_SITE S ) AND
...
The "new" execution plan is now:
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 110 | 661 (5)| 00:00:06 |
| 1 | SORT ORDER BY | | 1 | 110 | 661 (5)| 00:00:06 |
| 2 | NESTED LOOPS SEMI | | 1 | 110 | 655 (5)| 00:00:06 |
|* 3 | TABLE ACCESS BY INDEX ROWID | SUPPLIER_SCHEDULE_TAB | 1 | 82 | 654 (5)| 00:00:06 | |* 4 | INDEX FULL SCAN | SUPPLIER_SCHEDULE_UK | 1 | | 653 (5)| 00:00:06 | |* 5 | FILTER | | | | | |INDEX FAST FULL SCAN on USER_ALLOWED_SITE_B_IX, but now the execution plan is (at least for me) Much more readable.
| 6 | INDEX FAST FULL SCAN | USER_ALLOWED_SITE_B_IX | 1 | | 2 (0)| 00:00:01 |
| 7 | SORT AGGREGATE | | 1 | 39 | | |
|* 8 | COUNT STOPKEY | | | | | |
| 9 | FIRST ROW | | 1 | 39 | 3 (0)| 00:00:01 |
|* 10 | INDEX RANGE SCAN (MIN/MAX)| SUPPLIER_SCHEDULE_UK | 1 | 39 | 3 (0)| 00:00:01 | |* 11 | INDEX RANGE SCAN | SUPP_SCHED_AGREEMENT_PART_UK | 1 | 28 | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------------------------- The only difference is the FILTER step between INDEX FULL SCAN on SUPPLIER_SCHEDULE_UK and
My questions are :
1) Should I always trace (with event 10046 or 10053) the SQL to get the "full" (with all the steps) execution plan? 2) Is there any systematic "approach" (apart from trial & error) to reveal the hidden (the this case FLTER) steps ? 3) Why is "explain plan for" not able to ident properly parent/child steps ? Perhaps there is aBug related to LEVEL pseudo variable when using CONNECT BY ?
I am awaiting your comments impatiently ;)
Best Regards. Milen
P.S. There is no difference in the performance of both "versions" of this SQL
-- http://www.freelists.org/webpage/oracle-lReceived on Sun Feb 17 2008 - 09:14:56 CST