Topsy Turvy III - The Good Plan

Doug's Oracle Blog

  • Home
  • Papers
  • Books
  • C.V.
  • Fun
  • Blog

Apr 19: Topsy Turvy III - The Good Plan

I've been working at home recently on three different areas

  • Trying to get a demo of DUDE working with the able assistance of Kurt. I'll blog about DUDE later.
  • Looking at some Parallel Execution trace files with Jeff Moss to help decipher what's going on. There'll be more on that later, too.
  • Tons of (not always productive) analysis on the subquery performance problem at work the other week.

It's been a busy time and the latter has taken quite a while despite Jonathan Lewis' valuable help, both by email and by re-reading sections of Cost Based Oracle. In this blog I want to focus on a few apparent anomalies in the 'good' plan that resulted from using the no_unnest hint in the subquery, so here is the query, followed by the plan.

SELECT (a very long ...) column_list
FROM tab7, tab2, tab3, tab5, tab8, tab4, tab1, tab6, tab9
WHERE tab7.n11 = 3
AND tab7.n4 = tab2.n4
AND tab2.n5 = tab3.n5
AND tab3.n6 = tab5.n6
AND tab5.n8 IS NOT NULL
AND tab5.n6 = tab6.n6
AND tab6.n10 IS NULL
AND tab7.n12 = tab8.n13
AND tab8.n7 = tab4.n7
AND tab8.n7 = tab9.n7
AND tab8.n3 = tab1.n3
AND tab1.n1 = tab9.n1
AND tab1.n1 = tab6.n1
AND NVL (tab1.n2, 0) = 0
AND tab5.n8 = tab4.n8
AND tab4.n9 IN (SELECT /*+ no_unnest */ int_value
FROM v_extn_system_parameters
WHERE process_key = 'CAFTE003'
AND parameter_name LIKE 'ACCRUED%')


----------------------------------------------------------------------------------------
Id Operation Name Rows Bytes TempSpc Cost (%CPU)
----------------------------------------------------------------------------------------
0 SELECT STATEMENT 1206 167K 45991 (5)
* 1 FILTER
* 2 HASH JOIN 1206 167K 42373 (2)
* 3 HASH JOIN 1207 148K 42344 (2)
* 4 TABLE ACCESS FULL TAB5 9758 87822 22 (5)
* 5 HASH JOIN 16997 1942K 42321 (2)
6 INDEX FAST FULL SCAN TAB3_PK 9788 88092 9 (12)
* 7 HASH JOIN 16997 1792K 42312 (2)
8 TABLE ACCESS FULL TAB2 9788 88092 19 (6)
* 9 HASH JOIN 16896 1633K 42292 (2)
10 TABLE ACCESS FULL TAB9 2232 31248 7 (15)
*11 HASH JOIN 17017 1412K 4808K 42284 (2)
*12 HASH JOIN 71245 3965K 39878 (2)
*13 TABLE ACCESS FULL TAB1 44542 1000K 21422 (1)
*14 HASH JOIN 2492K 80M 18426 (4)
15 TABLE ACCESS FULL TAB4 56 784 7 (15)
16 TABLE ACCESS FULL TAB8 6031K 115M 18348 (3)
*17 TABLE ACCESS FULL TAB7 432K 11M 1343 (5)
*18 TABLE ACCESS FULL TAB6 19574 305K 28 (4)
19 REMOTE 1 47
---------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0
FROM "EXTN_SYSTEM_PARAMETERS"@ADM "EXTN_SYSTEM_PARAMETERS"
WHERE "EXTN_SYSTEM_PARAMETERS"."PROCESS_KEY"='CAFTE003'
AND "EXTN_SYSTEM_PARAMETERS"."INT_VALUE"=:B1
AND "EXTN_SYSTEM_PARAMETERS"."PARAMETER_NAME" LIKE 'ACCRUED%'))
2 - access("TAB5"."N6"="TAB6"."N6" AND "TAB1"."N1"="TAB5"."N1")
3 - access("TAB3"."N6"="TAB5"."N6" AND "TAB5"."N8"="SYS_ALIAS_1"."N8")
4 - filter("TAB5"."N8" IS NOT NULL)
5 - access("TAB2"."N5"="TAB3"."N5")
7 - access("TAB7"."N4"="TAB2"."N4")
9 - access("TAB8"."N7"="TAB9"."N7" AND "TAB1"."N1"="TAB9"."N1")
11 - access("TAB7"."N12"="TAB8"."N13")
12 - access("TAB8"."N3"="TAB1"."N3")
13 - filter(NVL("TAB1"."N2",0)=0)
14 - access("TAB8"."N7"="SYS_ALIAS_1"."N7")
17 - filter("TAB7"."N11"=3)
18 - filter("TAB6"."N10" IS NULL)


Remember that this plan worked well. However, Jonathan pointed out several interesting aspects of it which are covered in Chapter 9 of his book on 'Query Transformation'. I grasped the basic idea of what he was describing but found some of the details counter-intuitive. That's why I decided to describe my understanding and others can chip in with their comments.

Cardinality
In step 15 of the plan, the number of rows expected to be retrieved from TAB4 is 56. In fact this should be 1124, as shown in the original 'bad' plan.

15 TABLE ACCESS FULL TAB4 1124 15736 7 (15)

The 56 is 5% of 1124, which is the CBO's fixed estimate of how many rows will be returned by a filter on an unknown value. You can see the filter against TAB4 in step 1 (the :B1 is the unknown value). i.e. The CBO doesn't know how many rows will be filtered out so it assumes that the filter will only match 5% of the rows. In fact, nearly all of the rows in TAB4 match the filter so the assumption is wrong in this case. Bear in mind that this is the cardinality that the CBO is using to decide the join order, so I suppose this could lead to some very poor plans if this table had been bigger than in this example.


Updated - I just noticed the following thread at asktom which also discusses the 5% factor. Here's another one.

In Step 19 of the plan, the CBO is predicting it will retrieve 1 row from the remote table, when it should be 4 rows, as stated in the business requirement. This difference between the expected and real cardinality was also present in the 'bad' plan.


System-generated Alias
The Access Predicate for step 14 refers to "SYS_ALIAS_1"."N7" instead of "TAB4"."N7". The original un-obfuscated code does contain an alias for this table but Oracle has decided to override it with SYS_ALIAS_1 instead.

14 - access("TAB8"."N7"="SYS_ALIAS_1"."N7")

You can also see this SYS_ALIAS_1 being used in the Access Predicates for Step 3 - where TAB5.N8 should equal TAB4.N8

3 - access("TAB3"."N6"="TAB5"."N6" AND "TAB5"."N8"="SYS_ALIAS_1"."N8")


So SYS_ALIAS_1 is obviously something a little different to TAB4 and when I generated a 10053 trace file, it appeared at the first mention of TAB4. So even before the filter is applied, the CBO is aware that TAB4 will be subject to a filter as the result of the transformation and has a hidden inline view that needs an alias. What I found counter-intuitive is that, despite the fact that the filter has not been applied at steps 14/15/16, the CBO treats the cardinality as though it is being applied (the 5% estimate) and also generates this fake alias before it looks like it's needed.

A few closing thoughts (not technical)
The CBO is a constantly changing landscape. If there's a better example of the "Things you think you know" (Source - various), I can't think of it.

Execution plans are a representation of what the server is doing. They might be very close representations most of the time, but there are limitations and quirks.

Jonathan's book is excellent. I don't mind being repetitive because every time I read bits of it, my opinion of it improves. It can be a little hard to read, but only because there's so much information that you need to take it carefully and pay attention. One minute you're sailing along quite happily; you stop paying attention and 'Bang!', you have to retrace a couple of pages. Don't even think about trying it when you're full of flu ;-)

When I thought back to *why* I was so keen to use the /*+ no_unnest hint */ it was because I'd remembered from some of Jonathan's presentations that you could run into these types of problems when moving from 8i to 9i. Working at a site where we've had a large number of 9i upgrades recently has taught me that there's no way you should be planning large scale 9i upgrades without having good 8i environment availability during the process. It *is* possible to tune SQL statements that start to play up after a 9i upgrade, but *far* easier to be able to compare the code in 8i and 9i test environments to see what's changed. Be prepared for things to change and be prepared for Oracle Support to say 'How did the execution plan look before the upgrade?'. That can be quite a time-consuming question to answer if you don't have a matching 8i environment kicking around!

Posted by Doug Burns Comments: (0) Trackbacks: (0)

Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)

No comments


Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications
BBCode format allowed
 
 

Upcoming Appearances

Hotsos Symposium 2010 - 7th-11th March

Comments

Doug Burns about Advert: Symposium Countdown
Tue, 09.02.2010 09:03
Well I'll be running stuff in VMs, that's for sure, and people have gone on and [...]
Pete Scott about Advert: Symposium Countdown
Tue, 09.02.2010 08:55
It is such a relief to get the paper in (so well done, Doug).... I dispatched [...]
Doug Burns about Parallel Query and 11g
Sun, 07.02.2010 10:09
That could be a long reply, so [...]
Links in Comments

It's a minor source of frustration to me that you can't just paste a Hypertext link into the comments form here but, should you ever want to include a link, all you need to do is use the BBCode format, as mentioned below the comment form.

Here is a link to the relevant part of the document that explains how.

Bookmark

Open All | Close All

Syndicate This Blog

  • XML RSS 2.0 feed
  • ATOM/XML ATOM 1.0 feed
  • XML RSS 2.0 Comments
  • Feedburner Feed

Powered by

Serendipity PHP Weblog

Show tagged entries

xml 11g
xml ACE
xml adaptive thresholds
xml ash
xml Audit Vault
xml AWR
xml Blogging
xml Cuddly Toys
xml Database Refresh
xml Direct Path Reads
xml Fun
xml listener
xml locking
xml oow
xml oow2009
xml OTN
xml Parallel
xml Patching
xml Swingbench
xml The Reality Gap
xml Time Matters
xml Unix/Shell
xml Useful Links

Disclaimer

For the avoidance of any doubt, all views expressed here are my own and not those of past or current employers, clients, friends, Oracle Corporation, my Mum or, indeed, Flatcat. If you want to sue someone, I suggest you pick on Tigger, but I hope you have a good lawyer. Frankly, I doubt any of the former agree with my views or would want to be associated with them in any way.

Design by Andreas Viklund | Conversion to s9y by Carl