{"id":813,"date":"2010-04-19T12:00:00","date_gmt":"2010-04-19T12:00:00","guid":{"rendered":"http:\/\/orcldoug.com\/blog\/?p=813"},"modified":"2010-04-19T12:00:00","modified_gmt":"2010-04-19T12:00:00","slug":"topsy-turvy-iii-the-good-plan","status":"publish","type":"post","link":"http:\/\/orcldoug.com\/blog\/2010\/04\/19\/topsy-turvy-iii-the-good-plan\/","title":{"rendered":"Topsy Turvy III &#8211; The Good Plan"},"content":{"rendered":"<p>I&#8217;ve been working at home recently on three different areas <\/p>\n<ul>\n<li>Trying to get a demo of <a href=\"http:\/\/www.ora600.be\">DUDE<\/a> working with the able assistance of Kurt. I&#8217;ll blog about DUDE later.<\/li>\n<li>Looking at some Parallel Execution trace files with <a href=\"http:\/\/oramossoracle.blogspot.com\">Jeff Moss<\/a> to help decipher what&#8217;s going on. There&#8217;ll be more on that later, too.<\/li>\n<li>Tons of (not always productive) analysis on the <a href=\"http:\/\/oracledoug.blogspot.com\/2006\/04\/topsy-turvy-ii-plans_10.html\">subquery performance problem<\/a> at work the other week. <\/li>\n<\/ul>\n<p>It&#8217;s been a busy time and the latter has taken quite a while despite Jonathan Lewis&#8217; valuable help, both by email and by re-reading sections of <a href=\"http:\/\/www.jlcomp.demon.co.uk\/cbo_book\/ind_book.html\">Cost Based Oracle<\/a>. In this blog I want to focus on a few apparent anomalies in the &#8216;good&#8217; plan that resulted from using the no_unnest hint in the subquery, so here is the query, followed by the plan.<\/p>\n<pre>SELECT (a very long ...) column_list<br\/>FROM tab7, tab2, tab3, tab5, tab8, tab4, tab1, tab6, tab9<br\/>WHERE tab7.n11 = 3<br\/>AND tab7.n4 = tab2.n4<br\/>AND tab2.n5 = tab3.n5<br\/>AND tab3.n6 = tab5.n6<br\/>AND tab5.n8 IS NOT NULL<br\/>AND tab5.n6 = tab6.n6<br\/>AND tab6.n10 IS NULL<br\/>AND tab7.n12 = tab8.n13<br\/>AND tab8.n7 = tab4.n7<br\/>AND tab8.n7 = tab9.n7<br\/>AND tab8.n3 = tab1.n3<br\/>AND tab1.n1 = tab9.n1<br\/>AND tab1.n1 = tab6.n1<br\/>AND NVL (tab1.n2, 0) = 0<br\/>AND tab5.n8 = tab4.n8<br\/>AND tab4.n9 IN (SELECT \/*+ no_unnest *\/ int_value<br\/>FROM v_extn_system_parameters<br\/>WHERE process_key = 'CAFTE003'<br\/>AND parameter_name LIKE 'ACCRUED%')<br\/><p><br\/>----------------------------------------------------------------------------------------<br\/>Id   Operation                    Name                  Rows   Bytes TempSpc Cost (%CPU)<br\/>----------------------------------------------------------------------------------------<br\/>  0  SELECT STATEMENT                                    1206    167K        45991   (5)  <br\/>* 1   FILTER                                                                                   <br\/>* 2    HASH JOIN                                         1206    167K         42373  (2)  <br\/>* 3     HASH JOIN                                        1207    148K         42344  (2)  <br\/>* 4      TABLE ACCESS FULL       TAB5                    9758  87822            22   (5)  <br\/>* 5      HASH JOIN                                      16997   1942K        42321   (2)  <br\/>  6       INDEX FAST FULL SCAN   TAB3_PK                 9788  88092             9  (12)  <br\/>* 7       HASH JOIN                                     16997   1792K        42312   (2)    <br\/>  8        TABLE ACCESS FULL     TAB2                    9788  88092            19   (6) <br\/>* 9        HASH JOIN                                    16896   1633K        42292   (2)   <br\/> 10         TABLE ACCESS FULL    TAB9                    2232  31248             7  (15) <br\/>*11         HASH JOIN                                   17017   1412K  4808K 42284   (2) <br\/>*12          HASH JOIN                                  71245   3965K        39878   (2) <br\/>*13           TABLE ACCESS FULL  TAB1                   44542   1000K        21422   (1)  <br\/>*14           HASH JOIN                                  2492K    80M        18426   (4)   <br\/> 15            TABLE ACCESS FULL TAB4                      56    784             7  (15)<br\/> 16            TABLE ACCESS FULL TAB8                    6031K   115M        18348   (3)<br\/>*17          TABLE ACCESS FULL   TAB7                     432K    11M         1343   (5)<br\/>*18     TABLE ACCESS FULL        TAB6                   19574    305K           28   (4)  <br\/> 19    REMOTE                                               1     47                    <br\/>---------------------------------------------------<br\/>Predicate Information (identified by operation id):<br\/>---------------------------------------------------<br\/>1 - filter( EXISTS (SELECT \/*+ NO_UNNEST *\/ 0<br\/>         FROM \"EXTN_SYSTEM_PARAMETERS\"@ADM \"EXTN_SYSTEM_PARAMETERS\"<br\/>         WHERE \"EXTN_SYSTEM_PARAMETERS\".\"PROCESS_KEY\"='CAFTE003'<br\/>         AND \"EXTN_SYSTEM_PARAMETERS\".\"INT_VALUE\"=:B1<br\/>         AND \"EXTN_SYSTEM_PARAMETERS\".\"PARAMETER_NAME\" LIKE 'ACCRUED%'))<br\/>2 - access(\"TAB5\".\"N6\"=\"TAB6\".\"N6\" AND \"TAB1\".\"N1\"=\"TAB5\".\"N1\")<br\/>3 - access(\"TAB3\".\"N6\"=\"TAB5\".\"N6\" AND \"TAB5\".\"N8\"=\"SYS_ALIAS_1\".\"N8\")<br\/>4 - filter(\"TAB5\".\"N8\" IS NOT NULL)<br\/>5 - access(\"TAB2\".\"N5\"=\"TAB3\".\"N5\")<br\/>7 - access(\"TAB7\".\"N4\"=\"TAB2\".\"N4\")<br\/>9 - access(\"TAB8\".\"N7\"=\"TAB9\".\"N7\" AND \"TAB1\".\"N1\"=\"TAB9\".\"N1\")<br\/>11 - access(\"TAB7\".\"N12\"=\"TAB8\".\"N13\")<br\/>12 - access(\"TAB8\".\"N3\"=\"TAB1\".\"N3\")<br\/>13 - filter(NVL(\"TAB1\".\"N2\",0)=0)<br\/>14 - access(\"TAB8\".\"N7\"=\"SYS_ALIAS_1\".\"N7\")<br\/>17 - filter(\"TAB7\".\"N11\"=3)<br\/>18 - filter(\"TAB6\".\"N10\" IS NULL)<\/p><\/pre>\n<\/p>\n<p>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 &#8216;Query Transformation&#8217;. I grasped the basic idea of what he was describing but found some of the details counter-intuitive. That&#8217;s why I decided to describe my understanding and others can chip in with their comments.<\/p>\n<p><strong><u>Cardinality<\/u><\/strong><br \/>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 &#8216;bad&#8217; plan. <\/p>\n<\/p>\n<pre>15 TABLE ACCESS FULL TAB4 <strong>1124<\/strong> 15736 7 (15)<\/pre>\n<p>The 56 is 5% of 1124, which is the CBO&#8217;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&#8217;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.<span style=\"font-weight: bold\"><\/p>\n<p>Updated &#8211; I just noticed the following <a href=\"http:\/\/tinyurl.com\/gv3yf\">thread<\/a> at asktom which also discusses the 5% factor. Here&#8217;s <a href=\"http:\/\/asktom.oracle.com\/pls\/ask\/f?p=4950:8:13524714221431412403::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:34807121420113\">another one<\/a>.<\/p>\n<p><\/span>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 &#8216;bad&#8217; plan.<\/p>\n<p><strong><u>System-generated Alias<\/u><\/strong><br \/>The Access Predicate for step 14 refers to &#8220;SYS_ALIAS_1&#8221;.&#8221;N7&#8243; instead of &#8220;TAB4&#8243;.&#8221;N7&#8221;. The original un-obfuscated code does contain an alias for this table but Oracle has decided to override it with SYS_ALIAS_1 instead.<\/p>\n<pre>14 - access(\"TAB8\".\"N7\"=\"SYS_ALIAS_1\".\"N7\")<\/pre>\n<p>You can also see this SYS_ALIAS_1 being used in the Access Predicates for Step 3 &#8211; where TAB5.N8 should equal TAB4.N8<\/p>\n<pre>3 - access(\"TAB3\".\"N6\"=\"TAB5\".\"N6\" AND \"TAB5\".\"N8\"=\"SYS_ALIAS_1\".\"N8\")<\/pre>\n<p>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 <em>not<\/em> 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&#8217;s needed.<\/p>\n<p><strong><u>A few closing thoughts (not technical)<\/u><\/strong><br \/>The CBO is a constantly changing landscape. If there&#8217;s a better example of the &#8220;Things you <em>think<\/em> you know&#8221; (Source &#8211; various), I can&#8217;t think of it.<\/p>\n<p>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.<\/p>\n<p>Jonathan&#8217;s book is excellent. I don&#8217;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&#8217;s so much information that you need to take it carefully and pay attention. One minute you&#8217;re sailing along quite happily; you stop paying attention and &#8216;Bang!&#8217;, you have to retrace a couple of pages. Don&#8217;t even think about trying it when you&#8217;re full of flu &#128521;<\/p>\n<p>When I thought back to *why* I was so keen to use the \/*+ no_unnest hint *\/ it was because I&#8217;d remembered from some of Jonathan&#8217;s presentations that you could run into these types of problems when moving from 8i to 9i. Working at a site where we&#8217;ve had a large number of 9i upgrades recently has taught me that there&#8217;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&#8217;s changed. Be prepared for things to change and be prepared for Oracle Support to say &#8216;How did the execution plan look before the upgrade?&#8217;. That can be quite a time-consuming question to answer if you don&#8217;t have a matching 8i environment kicking around!<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;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&#8217;ll blog about DUDE later. Looking at some Parallel Execution trace files with Jeff Moss to help decipher what&#8217;s going on. There&#8217;ll be more on that later, too. Tons of (not&hellip; <a class=\"more-link\" href=\"http:\/\/orcldoug.com\/blog\/2010\/04\/19\/topsy-turvy-iii-the-good-plan\/\">Continue reading <span class=\"screen-reader-text\">Topsy Turvy III &#8211; The Good Plan<\/span><\/a><\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-813","post","type-post","status-publish","format-standard","hentry","category-uncategorized","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":1423,"url":"http:\/\/orcldoug.com\/blog\/2008\/08\/03\/dude-its-a-blog\/","url_meta":{"origin":813,"position":0},"title":"dude &#8211; it&#8217;s a blog","date":"August 3, 2008","format":false,"excerpt":"Well, I'm back from two weeks holiday away from the net and a pile of blog comments and emails to catch up on. I'll leave those for tomorrow. A quickie for now, though. I see that Kurt van Meerbeeck (yeah, OK, Kurt the DUDE guy) has finally started a blog.\u2026","rel":"","context":"Similar post","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1582,"url":"http:\/\/orcldoug.com\/blog\/2010\/03\/10\/hotsos-2010-day-3-an-excellent-one-part-1\/","url_meta":{"origin":813,"position":1},"title":"Hotsos 2010 &#8211; Day 3 &#8211; An excellent one (part 1)","date":"March 10, 2010","format":false,"excerpt":"Well, that was a nice start to the day! Someone came up to me just before Wolfgang Breitling's presentation to point out that something from my presentation had helped him fix a problem at work last night. It was the OEM Raw Data drill-down that shows you the underlying ASH\u2026","rel":"","context":"With 2 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1000,"url":"http:\/\/orcldoug.com\/blog\/2006\/06\/18\/saving-optimiser-stats-10g\/","url_meta":{"origin":813,"position":2},"title":"Saving Optimiser Stats &#8211; 10g","date":"June 18, 2006","format":false,"excerpt":"In my previous blog I showed how you can save your current optimiser stats into a seperate table whenever you refresh them, so that they can be restored should the new statistics lead to poor SQL execution plans. Oracle have added an improved version of this facility to 10g (which\u2026","rel":"","context":"Similar post","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1297,"url":"http:\/\/orcldoug.com\/blog\/2007\/07\/16\/todays-links\/","url_meta":{"origin":813,"position":3},"title":"Today&#8217;s Links","date":"July 16, 2007","format":false,"excerpt":"Here are a few interesting blog postings I've been reading this week.Jonathan Lewis on another Statspack example.Nuno Souto on SQL Server - in three parts. What I particularly like about these postings is that the man has used both and is honest about their relative strengths and weaknesses, rather than\u2026","rel":"","context":"With 3 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1446,"url":"http:\/\/orcldoug.com\/blog\/2008\/09\/29\/fleece-update\/","url_meta":{"origin":813,"position":4},"title":"Fleece Update","date":"September 29, 2008","format":false,"excerpt":"To those who have been badgering me about this for the best part of a week (they know who they are), it's time for more information about this year's Oracle ACE OOW gift.(Jacket by OTN, T-Shirt Model's Own, Photography Alex Gorbachev, Camera Frits Hoogland)It's not right.First of all, it's sophisticated\u2026","rel":"","context":"With 4 comments","img":{"alt_text":"","src":"\/serendipity\/uploads\/xaceback.jpg.pagespeed.ic.eFBD0upGTh.jpg","width":350,"height":200},"classes":[]},{"id":1453,"url":"http:\/\/orcldoug.com\/blog\/2008\/10\/04\/belgium-seminar-cancelled\/","url_meta":{"origin":813,"position":5},"title":"Belgium Seminar Cancelled","date":"October 4, 2008","format":false,"excerpt":"Unfortunately, the two day performance course I was due to be teaching in Belgium in a couple of weeks has been cancelled. It also means that I won't be getting to see the DUDE as soon as I thought \ud83d\ude41","rel":"","context":"With 3 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/posts\/813","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/comments?post=813"}],"version-history":[{"count":0,"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/posts\/813\/revisions"}],"wp:attachment":[{"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/media?parent=813"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/categories?post=813"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/orcldoug.com\/blog\/wp-json\/wp\/v2\/tags?post=813"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}