<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>OraStory</title>
	<atom:link href="http://orastory.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://orastory.wordpress.com</link>
	<description>Dominic Brooks on Data Quality, Sensible Design &#38; Performance ... (Now with added Sets Appeal)</description>
	<lastBuildDate>Mon, 23 Jan 2012 11:02:49 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='orastory.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>OraStory</title>
		<link>http://orastory.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://orastory.wordpress.com/osd.xml" title="OraStory" />
	<atom:link rel='hub' href='http://orastory.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Materialize</title>
		<link>http://orastory.wordpress.com/2012/01/17/materialize/</link>
		<comments>http://orastory.wordpress.com/2012/01/17/materialize/#comments</comments>
		<pubDate>Tue, 17 Jan 2012 10:48:39 +0000</pubDate>
		<dc:creator>Dom Brooks</dc:creator>
				<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11g]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[subquery factoring]]></category>
		<category><![CDATA[with clause]]></category>

		<guid isPermaLink="false">http://orastory.wordpress.com/?p=1423</guid>
		<description><![CDATA[Summary &#8211; Note the recursive SQL, the association of an in-memory temporary table with a child cursor, and possible side-effects for distributed transactions. Prompted by a recent thread on the OTN forums, if you /*+ materialize */ a subquery will you always get IO associated with that materialisation? Short answer: Yes. For that is what [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1423&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Summary &#8211; Note the recursive SQL, the association of an in-memory temporary table with a child cursor, and possible side-effects for distributed transactions.</p>
<p>Prompted by a <a href="https://forums.oracle.com/forums/message.jspa?threadID=2333002">recent thread</a> on the OTN forums, if you /*+ materialize */ a subquery will you always get IO associated with that materialisation?</p>
<p>Short answer: Yes. For that is what materialisation is all about.</p>
<p>A longer answer is perhaps slightly more interesting.</p>
<p>In terms of materialisation, you can force it with the hint above or it will automatically kick in if you reference it at least twice. I&#8217;m not aware of this threshold being documented but Jonathan Lewis mentioned this observation <a href="http://jonathanlewis.wordpress.com/2007/07/26/subquery-factoring-2/">here</a> and it ties in with what I&#8217;ve seen.</p>
<p>And it doesn&#8217;t seem to matter how small the result set is, it will always be materialised if those materialisation criteria are met.</p>
<p>If we trace a new query, we can see some of the recursive sql involved.</p>
<p><pre class="brush: plain;">
SQL&gt; alter session set events '10046 trace name context forever, level 12';
SQL&gt;
SQL&gt; with x as
  2  (select /*+ materialize */
  3          1 col1
  4   from   dual)
  5  select * from x;

      COL1
----------
         1

SQL&gt; alter session set events '10046 trace name context off';
SQL&gt;
</pre></p>
<p>Tracing older versions of Oracle can be more revealing because in 9iR2 for example the trace file explicitly lists the recursive INSERT into the temp table, whereas by the time you get to 11.2 the INSERT has disappeared and the associated waits incorporated into the SELECT.</p>
<p>All versions list the creation of the temporary table (if indeed it needs to be created &#8211; see below), the DDL for which includes the specifications IN_MEMORY_METADATA and CURSOR_SPECIFIC_SEGMENT.</p>
<p><pre class="brush: plain;">
CREATE GLOBAL TEMPORARY TABLE &quot;SYS&quot;.&quot;SYS_TEMP_0FD9D6610_8A97FC&quot; (&quot;C0&quot; NUMBER )
   IN_MEMORY_METADATA CURSOR_SPECIFIC_SEGMENT STORAGE (OBJNO 4254950928 )
  NOPARALLEL
</pre></p>
<p>Note in the creation of table SYS_TEMP_0FD9D6610_8A97FC that 0FD9D6610 is the hex of 4254950928, which is just the sequence-based objno. Not sure of the significance of the last part, e.g.8A97FC.</p>
<p>We can also see that the data is written to temp using a direct path write/direct path write temp depending on version &#8230; and selected back via the buffer cache (for efficient use of the data) using a db file sequential read or db file scattered read.</p>
<p>In older versions as mentioned, you should find the recursive INSERT listed separately, e.g. (different database, different version, different temp table name and if you&#8217;re interested in the control file sequential read see <a href="http://timurakhmadeev.wordpress.com/2010/01/23/control-file-sequential-read/">this post by Timur Akhmadeev</a>):</p>
<p><pre class="brush: plain;">
INSERT /*+ APPEND BYPASS_RECURSIVE_CHECK */ INTO 
  &quot;SYS&quot;.&quot;SYS_TEMP_0FD9D662B_671BC5CD&quot; SELECT /*+ */ 1 FROM &quot;SYS&quot;.&quot;DUAL&quot; 
  &quot;DUAL&quot;

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  control file sequential read                    3        0.00          0.00
  direct path write                               1        0.00          0.00
********************************************************************************
</pre></p>
<p>Otherwise in newer versions, no insert but the waits for the recursive statement listed as part of the main select:<br />
<pre class="brush: plain;">
with x as
(select /*+ materialize */
        1 col1
 from   dual)
select * from x 

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  Disk file operations I/O                        2        0.00          0.00
  direct path write temp                          1        0.00          0.00
  direct path sync                                1        0.02          0.02
  SQL*Net message to client                       2        0.00          0.00
  db file sequential read                         1        0.00          0.00
  SQL*Net message from client                     2       14.45         14.45
********************************************************************************

</pre></p>
<p>The temp table exists in memory and our session and other sessions cannot describe it but can select from it:</p>
<p><pre class="brush: plain;">
SQL&gt; desc  sys.SYS_TEMP_0FD9D6610_8A97FC;
ERROR:
ORA-04043: object sys.SYS_TEMP_0FD9D6610_8A97FC does not exist


SQL&gt; select * from sys.SYS_TEMP_0FD9D6610_8A97FC;

no rows selected

SQL&gt; 
</pre></p>
<p>Subsequent executions of this cursor can use this existing in-memory temporary table with no need to recreate it. </p>
<p>Note that the temp table is associated with the child cursor. This can be observed by using multiple sessions and forcing the creation of multiple child cursors &#8211; for example by using different optimizer settings &#8211; and tracing those sessions.</p>
<p>So, if we ran into one of the numerous situations that exist &#8211; often caused by bugs &#8211; where there are excessive child cursors for sql statements, if these use materialised subqueries then this is something else to be slightly concerned about.</p>
<p>If the cursor ages out or we flush the shared pool, the table will be cleaned up along with the cursor.</p>
<p><pre class="brush: plain;">
SQL&gt; alter system flush shared_pool;
SQL&gt; select * from sys.SYS_TEMP_0FD9D6610_8A97FC;
select * from sys.SYS_TEMP_0FD9D6610_8A97FC
                  *
ERROR at line 1:
ORA-00942: table or view does not exist


SQL&gt; 
</pre></p>
<p>This recursive creation of the temp table might raise some interesting questions. For example, how this (recursive DDL) might affect / be affected by transactions?</p>
<p>Short answer: It does and it doesn&#8217;t</p>
<p>The longer answer is that it only seems to affect distributed transactions and this effect is apparently a bug or bugs, separately listed in both 10.2 &#8211; bug 9399589 &#8211; and 11.1/11.2 &#8211; bug 9706532.</p>
<p>I&#8217;ve not tested the proposed patches to the issue, but certainly what happens in 11.2.0.3 is that if you hard-parse the statement as part of a distributed transaction, then the materialisation is silently bypassed.<br />
<pre class="brush: plain;">
SQL&gt; alter system flush shared_pool;
SQL&gt; -- distributed transaction
SQL&gt; insert into t1@test values(1);
SQL&gt; with x as
  2  (select /*+ materialize */
  3          1
  4   from   dual)
  5  select * from x;

         1
----------
         1
SQL&gt; select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------
SQL_ID  0x0a8cyg22wz7, child number 0
-------------------------------------
with x as (select /*+ materialize */         1  from   dual) select *
from x

Plan hash value: 1388734953

-----------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |
-----------------------------------------------------------------
|   0 | SELECT STATEMENT |      |       |     2 (100)|          |
|   1 |  FAST DUAL       |      |     1 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------

SQL&gt; commit;
SQL&gt; -- no distributed transaction
SQL&gt; with x as
  2  (select /*+ materialize */
  3          1
  4   from   dual)
  5  select * from x;

         1
----------
         1
SQL&gt; 
SQL&gt; select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------
SQL_ID  0x0a8cyg22wz7, child number 0
-------------------------------------
with x as (select /*+ materialize */         1  from   dual) select *
from x

Plan hash value: 1388734953

-----------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |
-----------------------------------------------------------------
|   0 | SELECT STATEMENT |      |       |     2 (100)|          |
|   1 |  FAST DUAL       |      |     1 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------

SQL&gt; 


</pre></p>
<p>Whereas if it&#8217;s a local transaction that does the hard-parse then materialisation can be used and subsequent executions of that cursor in a distributed transaction can make use of that plan and the existing temp table.</p>
<p><pre class="brush: plain;">
SQL&gt; alter system flush shared_pool;

System altered.

SQL&gt; -- no distributed transaction
SQL&gt; with x as
  2  (select /*+ materialize */
  3          1
  4   from   dual)
  5  select * from x;

         1
----------
         1

1 row selected.

SQL&gt; select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------
SQL_ID  0x0a8cyg22wz7, child number 0
-------------------------------------
with x as (select /*+ materialize */         1  from   dual) select *
from x

Plan hash value: 3267439756

---------------------------------------------------------------------------------------------
| Id  | Operation                  | Name                      | Rows  | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |                           |       |       |     4 (100)|
|   1 |  TEMP TABLE TRANSFORMATION |                           |       |       |            |
|   2 |   LOAD AS SELECT           |                           |       |       |            |
|   3 |    FAST DUAL               |                           |     1 |       |     2   (0)|
|   4 |   VIEW                     |                           |     1 |     3 |     2   (0)|
|   5 |    TABLE ACCESS FULL       | SYS_TEMP_0FD9D6604_8B16D2 |     1 |    13 |     2   (0)|
---------------------------------------------------------------------------------------------


18 rows selected.

SQL&gt; -- distributed transaction
SQL&gt; insert into t1@test values(1);

1 row created.

SQL&gt; with x as
  2  (select /*+ materialize */
  3          1
  4   from   dual)
  5  select * from x;

         1
----------
         1

1 row selected.

SQL&gt; select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------
SQL_ID  0x0a8cyg22wz7, child number 0
-------------------------------------
with x as (select /*+ materialize */         1  from   dual) select *
from x

Plan hash value: 3267439756

---------------------------------------------------------------------------------------------
| Id  | Operation                  | Name                      | Rows  | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |                           |       |       |     4 (100)|
|   1 |  TEMP TABLE TRANSFORMATION |                           |       |       |            |
|   2 |   LOAD AS SELECT           |                           |       |       |            |
|   3 |    FAST DUAL               |                           |     1 |       |     2   (0)|
|   4 |   VIEW                     |                           |     1 |     3 |     2   (0)|
|   5 |    TABLE ACCESS FULL       | SYS_TEMP_0FD9D6604_8B16D2 |     1 |    13 |     2   (0)|
---------------------------------------------------------------------------------------------


18 rows selected.

SQL&gt; 
</pre></p>
<p>I saw on Twitter last week and this week that <a href="http://www.orchestrapit.co.uk">@Boneist</a> had an interesting experience with this sort of thing.</p>
<p>Finally, as a quick related distraction, note that if you try to get a real time sql monitoring report within a distributed transaction &#8211; I mean, why would you? but anyway I found this whilst investing the distributed behaviour above &#8211; then it will bomb out with ORA-32036: unsupported case for inlining of query name in WITH clause.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/orastory.wordpress.com/1423/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/orastory.wordpress.com/1423/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/orastory.wordpress.com/1423/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/orastory.wordpress.com/1423/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/orastory.wordpress.com/1423/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/orastory.wordpress.com/1423/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/orastory.wordpress.com/1423/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/orastory.wordpress.com/1423/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/orastory.wordpress.com/1423/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/orastory.wordpress.com/1423/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/orastory.wordpress.com/1423/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/orastory.wordpress.com/1423/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/orastory.wordpress.com/1423/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/orastory.wordpress.com/1423/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1423&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://orastory.wordpress.com/2012/01/17/materialize/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">dombrooks</media:title>
		</media:content>
	</item>
		<item>
		<title>ORA-32035 considered a good thing?</title>
		<link>http://orastory.wordpress.com/2012/01/12/ora-32305-considered-a-good-thing/</link>
		<comments>http://orastory.wordpress.com/2012/01/12/ora-32305-considered-a-good-thing/#comments</comments>
		<pubDate>Thu, 12 Jan 2012 13:16:38 +0000</pubDate>
		<dc:creator>Dom Brooks</dc:creator>
				<category><![CDATA[11gR2]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[subquery factoring]]></category>
		<category><![CDATA[with clause]]></category>

		<guid isPermaLink="false">http://orastory.wordpress.com/?p=1415</guid>
		<description><![CDATA[I used to think that it was a good thing that this error was no longer raised in 11.2. Now I&#8217;m not so sure. Doh! Note to self: Clear up old Ts, T1s, T2s, etc straight afterwards. I used to ALWAYS prefix my factored subqueries as subq_*. This provides a compelling reason to continue that [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1415&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I used to think that it was a good thing that this error was no longer raised in 11.2.</p>
<p>Now I&#8217;m not so sure.</p>
<p><pre class="brush: plain;">
SQL&gt; select * from v$version;

BANNER
-------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE    11.2.0.3.0      Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

SQL&gt; with t as
  2  (select 1 col2 from dual)
  3  select * from t1;

COL1
-----
XXXXX

SQL&gt; 
</pre></p>
<p>Doh!</p>
<p>Note to self: Clear up old Ts, T1s, T2s, etc straight afterwards.</p>
<p>I used to ALWAYS prefix my factored subqueries as subq_*.</p>
<p>This provides a compelling reason to continue that &#8211; not that it changes anything of course, just means I&#8217;m less likely to make such a typo.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/orastory.wordpress.com/1415/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/orastory.wordpress.com/1415/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/orastory.wordpress.com/1415/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/orastory.wordpress.com/1415/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/orastory.wordpress.com/1415/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/orastory.wordpress.com/1415/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/orastory.wordpress.com/1415/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/orastory.wordpress.com/1415/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/orastory.wordpress.com/1415/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/orastory.wordpress.com/1415/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/orastory.wordpress.com/1415/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/orastory.wordpress.com/1415/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/orastory.wordpress.com/1415/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/orastory.wordpress.com/1415/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1415&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://orastory.wordpress.com/2012/01/12/ora-32305-considered-a-good-thing/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">dombrooks</media:title>
		</media:content>
	</item>
		<item>
		<title>Sql tuning request</title>
		<link>http://orastory.wordpress.com/2012/01/11/sql-tuning-request/</link>
		<comments>http://orastory.wordpress.com/2012/01/11/sql-tuning-request/#comments</comments>
		<pubDate>Wed, 11 Jan 2012 15:40:23 +0000</pubDate>
		<dc:creator>Dom Brooks</dc:creator>
				<category><![CDATA[11gR2]]></category>
		<category><![CDATA[cbo]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[sql tuning]]></category>

		<guid isPermaLink="false">http://orastory.wordpress.com/?p=1339</guid>
		<description><![CDATA[Without knowing anything about the problem in advance, I thought it would be good to do a walkthrough post of a sql tuning request. But now that I&#8217;m done I&#8217;m unconvinced as it&#8217;s probably too long, the query too meaningless and the real time sql monitoring text output too unreadable in a blog post. I&#8217;m [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1339&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Without knowing anything about the problem in advance, I thought it would be good to do a walkthrough post of a sql tuning request.</p>
<p>But now that I&#8217;m done I&#8217;m unconvinced as it&#8217;s probably too long, the query too meaningless and the real time sql monitoring text output too unreadable in a blog post.</p>
<p>I&#8217;m always reluctant to post real, specific application issues because I&#8217;m never sure how well they translate and illustrate the desired points unless you convert them to a standalone test case.</p>
<p>Let&#8217;s see how it goes.</p>
<ul>
<li>The idea is to touch on the broad strategies that I&#8217;m going through.</li>
<li>The scope of the solution should match the scope of the problem &#8211; so for a single problem query, table design and current indexes, stats and histograms should be considered as set in stone.</li>
<li>Ideally we want to avoid hinting as much as possible or at least stick to <a href="http://orastory.wordpress.com/2011/12/12/hints-of-acceptability/">acceptable hints</a>.</li>
<li>And if we&#8217;re going to manually intervene, we&#8217;re not particularly interested in what the current production plan is nor the usage of any plan stability features to preserve it (unless it&#8217;s better than we can do ourselves).</li>
</ul>
<p>This is from an 11gR2 testing environment, 11.2.0.3 to be specific.</p>
<p>Having done the latest merge of a production code release (9.2.0.8) into our 11gR2 environment (upgrade ETA March), a report has been reported as slow, taking about 1.5 minutes in production and some 20 minutes in the 11gR2 environment.</p>
<p>So, having traced the report and found that the driving query is the prime suspect, let&#8217;s get some feedback on the performance of the SQL statement.</p>
<p>Here&#8217;s the original sql statement to give some context &#8211; I&#8217;ve commented out most of the columns because they don&#8217;t add much other than just length to the post.</p>
<p><pre class="brush: plain;">
SELECT ... some columns ...,
       ... a function call ...,
       ... some more columns ...
FROM   isbk,
       inst,
       bsta,
       isco,
       sdol,
       borg,
       book,
       isdm,
       rule
WHERE  book.book_num = rule.book_num
AND    rule.rule_type_code = 'IBK'
AND    isbk.inst_num = inst.inst_num
AND    inst.inst_num = bsta.inst_num
AND    inst.inst_num = isco.inst_num
AND    isco.inst_num = isdm.inst_num
AND    isco.xcod_code = 'SEDL'
AND    isco.inst_num = sdol.inst_num
AND    sdol.xcod_code = 'ISIN'
AND    borg.borg_num = inst.issuer_num
AND    book.book_num = isbk.book_num
AND    bsta.current_mat_date &gt;= TO_DATE(v_bus_date, 'yyyymmdd')
UNION ALL
SELECT ... some columns ...
FROM   inst, 
       bsta,
       isco,
       sdol,
       borg,
       isdm,
       inix
WHERE  inst.inst_num = bsta.inst_num
AND    inst.inst_num = isco.inst_num
AND    isco.inst_num = isdm.inst_num
AND    isco.xcod_code = 'SEDL'
AND    isco.inst_num = sdol.inst_num
AND    sdol.xcod_code = 'ISIN'
AND    borg.borg_num = inst.issuer_num
AND    bsta.current_mat_date &gt;= TO_DATE(v_bus_date, 'yyyymmdd')
AND    isco.inst_num = inix.inst_num
AND    inix.xcod_code = 'NIX'
AND    NOT EXISTS (SELECT 1
                   FROM   isbk,
                          rule,
                          book,
                          bsta
                   WHERE  rule.rule_type_code = 'IBK'
                   AND    book.book_num = rule.book_num
                   AND    book.book_num = isbk.book_num
                   AND    inst.inst_num = isbk.inst_num
                   AND    inst.inst_num = bsta.inst_num
                   AND    bsta.current_mat_date &gt;= TO_DATE(v_bus_date, 'yyyymmdd'));
</pre></p>
<p>Looking at the query and the repetition of tables and joins between the two UNION ALL parts, it looks like a classic case of two sets of disparate driving data that then need to be joined to the same additional tables.</p>
<p>Ironically, expanding ORs out into UNIONs is a common initial tuning step for performance problems with OR predicates.</p>
<p>Here&#8217;s what little extra schema knowledge might be useful:</p>
<ul>
<li>INST has a pk of INST_NUM.</li>
<li>ISCO has an n:1 relationship with INST.</li>
<li>ISDM has an n:1 relationship with INST.</li>
<li>BORG has a 1:1 relationship with INST.ISSUER_NUM.</li>
<li>BSTA has a 1:1 relationship with INST.</li>
<li>ISBK has a n:1 relationship with INST and a n:1 relationship with BOOK.</li>
<li>BOOK has a pk of BOOK_NUM.</li>
<li>RULE is a table used for generic filtering rules, in this case related to BOOK.BOOK_NUM.</li>
</ul>
<p>Let&#8217;s use Real-Time SQL Monitoring (usual license caveats apply) to see what the current performance story is. </p>
<p><pre class="brush: plain;">
SELECT dbms_sqltune.report_sql_monitor(&lt;sql_id&gt;) FROM DUAL;
</pre></p>
<p>If there&#8217;s one downside to RTSM, it&#8217;s a bit too wide for these blog posts <img src='http://s0.wp.com/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' /><br />
Maybe the RTSM pictures would have been better?</p>
<p>Anyway, I&#8217;ve sacrificed some of the columns in the report and tried to shrink the font (there&#8217;s a scrollbar at the bottom of the plan window).</p>
<pre>
<font size="-1">
Global Stats
===========================================================
| Elapsed |   Cpu   | PL/SQL  |  Other   | Fetch | Buffer |
| Time(s) | Time(s) | Time(s) | Waits(s) | Calls |  Gets  |
===========================================================
|    1094 |    1094 |    0.29 |     0.20 |    60 |   257K |
===========================================================

SQL Plan Monitoring Details (Plan Hash Value=1036829859)
===============================================================================================================================
| Id |                Operation                |       Name         |  Rows   | Execs |   Rows   | Activity | Activity Detail |
|    |                                         |                    | (Estim) |       | (Actual) |   (%)    |   (# samples)   |
===============================================================================================================================
|  0 | SELECT STATEMENT                        |                    |         |     1 |    29225 |          |                 |
|  1 |   NESTED LOOPS                          |                    |       1 |     1 |    29225 |          |                 |
|  2 |    NESTED LOOPS                         |                    |       1 |     1 |    29205 |          |                 |
|  3 |     NESTED LOOPS                        |                    |       1 |     1 |    29205 |          |                 |
|  4 |      VIEW                               | VW_JF_SET$AD8EBC08 |    6147 |     1 |    29205 |          |                 |
|  5 |       UNION-ALL                         |                    |         |     1 |    29205 |          |                 |
|  6 |        NESTED LOOPS                     |                    |       9 |     1 |    28719 |          |                 |
|  7 |         NESTED LOOPS                    |                    |      53 |     1 |    28722 |          |                 |
|  8 |          NESTED LOOPS                   |                    |     370 |     1 |    31674 |          |                 |
|  9 |           NESTED LOOPS                  |                    |     370 |     1 |    31674 |          |                 |
| 10 |            NESTED LOOPS                 |                    |       3 |     1 |        1 |          |                 |
| 11 |             INDEX RANGE SCAN            | RULE_IDX_2         |       3 |     1 |        1 |          |                 |
| 12 |             TABLE ACCESS BY INDEX ROWID | BOOK               |       1 |     1 |        1 |          |                 |
| 13 |              INDEX UNIQUE SCAN          | BOOK_IDX           |       1 |     1 |        1 |          |                 |
| 14 |            INDEX RANGE SCAN             | ISBK_IDX           |     116 |     1 |    31674 |          |                 |
| 15 |           TABLE ACCESS BY INDEX ROWID   | INST               |       1 | 31674 |    31674 |          |                 |
| 16 |            INDEX UNIQUE SCAN            | ISTR_PK            |       1 | 31674 |    31674 |          |                 |
| 17 |          TABLE ACCESS BY INDEX ROWID    | BSTA               |       1 | 31674 |    28722 |          |                 |
| 18 |           INDEX UNIQUE SCAN             | BSTAC_IDX          |       1 | 31674 |    31654 |          |                 |
| 19 |         INDEX RANGE SCAN                | ISCO_4_IDX         |       1 | 28722 |    28719 |          |                 |
| 20 |        HASH JOIN                        |                    |    6138 |     1 |      486 |          |                 |
| 21 |         HASH JOIN ANTI                  |                    |    5716 |     1 |    54961 |    67.16 | Cpu (732)       |
| 22 |          HASH JOIN                      |                    |   24385 |     1 |    83680 |          |                 |
| 23 |           INDEX RANGE SCAN              | ISCO_4_IDX         |    170K |     1 |     377K |          |                 |
| 24 |           HASH JOIN                     |                    |    144K |     1 |     266K |          |                 |
| 25 |            TABLE ACCESS FULL            | BSTA               |    144K |     1 |     266K |     0.09 | Cpu (1)         |
| 26 |            TABLE ACCESS FULL            | INST               |      1M |     1 |       1M |          |                 |
| 27 |          VIEW                           | VW_SQ_1            |     53M |     1 |       4G |     5.60 | Cpu (61)        |
| 28 |           HASH JOIN                     |                    |     53M |     1 |       4G |    27.06 | Cpu (295)       |
| 29 |            MERGE JOIN CARTESIAN         |                    |    458K |     1 |     266K |          |                 |
| 30 |             NESTED LOOPS                |                    |       3 |     1 |        1 |          |                 |
| 31 |              INDEX RANGE SCAN           | RULE_IDX_2         |       3 |     1 |        1 |          |                 |
| 32 |              INDEX UNIQUE SCAN          | BOOK_IDX           |       1 |     1 |        1 |          |                 |
| 33 |             BUFFER SORT                 |                    |    144K |     1 |     266K |          |                 |
| 34 |              INDEX FAST FULL SCAN       | BSTA_IDX_2         |    144K |     1 |     266K |          |                 |
| 35 |            TABLE ACCESS FULL            | ISBK               |      2M |     1 |       2M |          |                 |
| 36 |         INDEX RANGE SCAN                | ISCO_4_IDX         |    170K |     1 |    32983 |          |                 |
| 37 |      TABLE ACCESS BY INDEX ROWID        | BORG               |       1 | 29205 |    29205 |          |                 |
| 38 |       INDEX UNIQUE SCAN                 | BORG_PK            |       1 | 29205 |    29205 |          |                 |
| 39 |     INDEX RANGE SCAN                    | ISCO_4_IDX         |       1 | 29205 |    29205 |          |                 |
| 40 |    INDEX RANGE SCAN                     | ISDM_IDX           |       1 | 29205 |    29225 |          |                 |
===============================================================================================================================
</font>
</pre>
<p>There&#8217;s a lot in this report.</p>
<p>First thing you might have spotted is the <a href="http://orastory.wordpress.com/2011/06/07/join-factorization/">Join Factorisation</a> going on @ step 4 as indicated by the name VW_JF*.</p>
<p>Maybe you&#8217;re always wary of the MERGE JOIN CARTESIAN &#8230; BUFFER SORT? Not always a problem of course but where there&#8217;s trouble you&#8217;ll often find her (in real production scenarios, I find that this mechanism is a problem not so much when there are missing join conditions &#8211; because these are rarely found in production code &#8211; but rather as a valid join mechanism but where the rowsource estimates are significantly inaccurate).</p>
<p>If you look at the SQL, you&#8217;ll see a function call as well.</p>
<p>There are also a whole bunch of estimates that are significantly off.</p>
<p>Where to start?</p>
<p>There are four main areas of questioning:</p>
<ol>
<li>What&#8217;s taking all the time? Are there some particular steps in the plan which are more problematic than others?</li>
<li>If estimates are inaccurate, where do they go most wrong or go wrong first?</li>
<li>Which predicates eliminate the most data? i.e. it&#8217;s rarely a good thing to join thousands upon thousands of rows only to do a late filter in the plan to reduce it down to a few handfuls. Aka eliminate early.</li>
<li>What is the simplest / quickest / least invasive change that can be made to significantly improve performance? And will it actually be sufficient?</li>
</ol>
<p>From &#8220;Activity %&#8221;, I hope it&#8217;s clear from the report that all the time is taken up by the <strong>bottom half of the UNION ALL</strong>.</p>
<ul>
<li>The rowsource cardinality estimates are not that accurate.</li>
<li>Plus we find in that second half our old friend the MJC+BS.</li>
<li>And we&#8217;re just burning CPU down there.</li>
</ul>
<p>So, I&#8217;d like to isolate that bottom half of the UNION ALL and run it standalone.</p>
<p>However, in a clear indication of the issues with it, it won&#8217;t run standalone &#8211; It&#8217;s just blown 30+ gig of temp space.</p>
<p>But it returns only 486 rows so let&#8217;s try to get a runnable standalone version.</p>
<p>Before we start looking at possible solutions, let&#8217;s start to ask questions about the query logic itself, keeping an eye out for redundant tables and joins and asking ourselves whether this is the best way to word the question we think is being asked.</p>
<p>Best way to start that is <a href="http://orastory.wordpress.com/2010/03/24/tuning_by_formatting/" title="SQL Tuning By Formatting"></a></p>
<p>So, we&#8217;ve got a few &#8220;filters&#8221; predicates, a couple of &#8220;join predicates&#8221; and a NOT EXISTS correlated subquery.</p>
<p>The logic of the subquery is the first thing that jumps out at me.<br />
1. BOOK seems redundant<br />
We join RULE to BOOK and BOOK to ISBK all by BOOK_NUM and ISBK is correlated to the outer INST by INST_NUM.<br />
We do no filtering by any BOOK attribute so it serves no purpose so, let&#8217;s remove BOOK, join RULE straight to ISBK.</p>
<p>2. The subquery filtering by BSTA.CURRENT_MAT_DATE is irrelevant.<br />
This is the same filter as in the outer select.<br />
It doesn&#8217;t make sense.<br />
Ignoring the actual evaluation order of all these predicates, we should effectively only be checking the NOT EXISTS against INST_NUMS that have passed the outer BSTA filter. So, by definition, this particular predicate in the subquery will always be true. Why repeat it? This is a mistake.<br />
The only thing this subquery should be doing is checking they&#8217;re not in the RULE/ISBK combo. So, let&#8217;s remove that.</p>
<p>If we run just comment out the tables and joins as per suggestion above, then that bottom query runs in a couple of seconds:</p>
<pre>
<font size="-1">
Global Stats
=================================================
| Elapsed |   Cpu   |  Other   | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls |  Gets  |
=================================================
|    1.12 |    1.12 |     0.00 |     2 |  73879 |
=================================================

SQL Plan Monitoring Details (Plan Hash Value=2734204492)
==========================================================================================================
| Id |         Operation          |   Name     |  Rows   | Execs |   Rows   | Activity | Activity Detail |
|    |                            |            | (Estim) |       | (Actual) |   (%)    |   (# samples)   |
==========================================================================================================
|  0 | SELECT STATEMENT           |            |         |     1 |      489 |          |                 |
|  1 |   HASH JOIN RIGHT ANTI     |            |   38162 |     1 |      489 |          |                 |
|  2 |    VIEW                    | VW_SQ_1    |     555 |     1 |    31674 |          |                 |
|  3 |     NESTED LOOPS           |            |     555 |     1 |    31674 |          |                 |
|  4 |      INDEX RANGE SCAN      | RULE_IDX_2 |       3 |     1 |        1 |          |                 |
|  5 |      INDEX RANGE SCAN      | ISBK_IDX   |     176 |     1 |    31674 |          |                 |
|  6 |    HASH JOIN               |            |   38189 |     1 |    28700 |          |                 |
|  7 |     HASH JOIN              |            |   37626 |     1 |    28700 |          |                 |
|  8 |      HASH JOIN             |            |   35036 |     1 |    86956 |   100.00 | Cpu (1)         |
|  9 |       HASH JOIN            |            |   32625 |     1 |    87212 |          |                 |
| 10 |        HASH JOIN           |            |   31565 |     1 |    83593 |          |                 |
| 11 |         INDEX RANGE SCAN   | ISCO_4_IDX |    170K |     1 |     377K |          |                 |
| 12 |         HASH JOIN          |            |    144K |     1 |     266K |          |                 |
| 13 |          TABLE ACCESS FULL | BSTA       |    144K |     1 |     266K |          |                 |
| 14 |          TABLE ACCESS FULL | INST       |    796K |     1 |     796K |          |                 |
| 15 |        TABLE ACCESS FULL   | ISDM       |      1M |     1 |       1M |          |                 |
| 16 |       INDEX RANGE SCAN     | ISCO_4_IDX |    170K |     1 |     861K |          |                 |
| 17 |      INDEX RANGE SCAN      | ISCO_4_IDX |    170K |     1 |    32983 |          |                 |
| 18 |     TABLE ACCESS FULL      | BORG       |    328K |     1 |     328K |          |                 |
==========================================================================================================
</font>
</pre>
<p>Note that I&#8217;ve had to hint this with the /*+ monitor */ hint because by default this now executes beneath the default threshold for monitoring.</p>
<p>It looks like we&#8217;ve arrived at our quickest/simplest change just by going through the query quickly trying to understand the question it&#8217;s asking. No hints required.</p>
<p>So this is would be a good place to stop, thoroughly test the change, validate the original and the changed results and move on to something else.</p>
<p>We should drop it back into the UNION ALL and see what we get:</p>
<pre>
<font size="-1">
Global Stats
===========================================================
| Elapsed |   Cpu   | PL/SQL  |  Other   | Fetch | Buffer |
| Time(s) | Time(s) | Time(s) | Waits(s) | Calls |  Gets  |
===========================================================
|    4.68 |    4.68 |    0.38 |     0.00 |    60 |   230K |
===========================================================

SQL Plan Monitoring Details (Plan Hash Value=4171599168)
===============================================================================================================================
| Id |                Operation                |       Name         |  Rows   | Execs |   Rows   | Activity | Activity Detail |
|    |                                         |                    | (Estim) |       | (Actual) |   (%)    |   (# samples)   |
===============================================================================================================================
|  0 | SELECT STATEMENT                        |                    |         |     1 |    29225 |          |                 |
|  1 |   NESTED LOOPS                          |                    |       1 |     1 |    29225 |          |                 |
|  2 |    NESTED LOOPS                         |                    |       1 |     1 |    29205 |          |                 |
|  3 |     HASH JOIN                           |                    |       1 |     1 |    29205 |          |                 |
|  4 |      TABLE ACCESS FULL                  | BORG               |    328K |     1 |     328K |          |                 |
|  5 |      VIEW                               | VW_JF_SET$AD8EBC08 |   26182 |     1 |    29205 |          |                 |
|  6 |       UNION-ALL                         |                    |         |     1 |    29205 |          |                 |
|  7 |        NESTED LOOPS                     |                    |       9 |     1 |    28719 |          |                 |
|  8 |         NESTED LOOPS                    |                    |      53 |     1 |    28722 |          |                 |
|  9 |          NESTED LOOPS                   |                    |     370 |     1 |    31674 |          |                 |
| 10 |           NESTED LOOPS                  |                    |     370 |     1 |    31674 |          |                 |
| 11 |            NESTED LOOPS                 |                    |       3 |     1 |        1 |          |                 |
| 12 |             INDEX RANGE SCAN            | RULE_IDX_2         |       3 |     1 |        1 |          |                 |
| 13 |             TABLE ACCESS BY INDEX ROWID | BOOK               |       1 |     1 |        1 |          |                 |
| 14 |              INDEX UNIQUE SCAN          | BOOK_IDX           |       1 |     1 |        1 |          |                 |
| 15 |            INDEX RANGE SCAN             | ISBK_IDX           |     116 |     1 |    31674 |          |                 |
| 16 |           TABLE ACCESS BY INDEX ROWID   | INST               |       1 | 31674 |    31674 |          |                 |
| 17 |            INDEX UNIQUE SCAN            | ISTR_PK            |       1 | 31674 |    31674 |          |                 |
| 18 |          TABLE ACCESS BY INDEX ROWID    | BSTA               |       1 | 31674 |    28722 |          |                 |
| 19 |           INDEX UNIQUE SCAN             | BSTA_IDX           |       1 | 31674 |    31654 |          |                 |
| 20 |         INDEX RANGE SCAN                | ISCO_4_IDX         |       1 | 28722 |    28719 |          |                 |
| 21 |        HASH JOIN RIGHT ANTI             |                    |   26173 |     1 |      486 |          |                 |
| 22 |         VIEW                            | VW_SQ_1            |     555 |     1 |    31674 |          |                 |
| 23 |          NESTED LOOPS                   |                    |     555 |     1 |    31674 |          |                 |
| 24 |           INDEX RANGE SCAN              | RULE_IDX_2         |       3 |     1 |        1 |          |                 |
| 25 |           INDEX RANGE SCAN              | ISBK_IDX           |     176 |     1 |    31674 |          |                 |
| 26 |         HASH JOIN                       |                    |   26187 |     1 |    28693 |          |                 |
| 27 |          HASH JOIN                      |                    |   24385 |     1 |    83680 |    33.33 | Cpu (1)         |
| 28 |           INDEX RANGE SCAN              | ISCO_4_IDX         |    170K |     1 |     377K |          |                 |
| 29 |           HASH JOIN                     |                    |    144K |     1 |     266K |          |                 |
| 30 |            TABLE ACCESS FULL            | BSTA               |    144K |     1 |     266K |          |                 |
| 31 |            TABLE ACCESS FULL            | INST               |      1M |     1 |       1M |          |                 |
| 32 |          INDEX RANGE SCAN               | ISCO_4_IDX         |    170K |     1 |    32983 |          |                 |
| 33 |     INDEX RANGE SCAN                    | ISCO_4_IDX         |       1 | 29205 |    29205 |          |                 |
| 34 |    INDEX RANGE SCAN                     | ISDM_IDX           |       1 | 29205 |    29225 |          |                 |
===============================================================================================================================
</font>
</pre>
<p>So we could leave it there.</p>
<p>However, I wouldn&#8217;t blame you if you wanted to go further, even if we&#8217;re bordering on Compulsive Tuning Disorder:</p>
<ul>
<li>I don&#8217;t really like leaving behind a plan that&#8217;s got significantly inaccurate estimates &#8211; it leaves behind too much of a future threat.</li>
<li>And maybe we can also look at the original query and the direction the Join Factorisation was indicating and take it further? How about doing the UNION ALL a bit earlier and then do one lot of joining to the shared tables from the original SQL?</li>
<li>Let&#8217;s use some dynamic sampling to improve some single table cardinality estimates but then perhaps let&#8217;s go too far and add some join estimate adjustments (I say too far because I&#8217;m going to use opt_estimate but it&#8217;s undocumented and I&#8217;m not recommending it but if you do use it, then use it in conjunction with qb_name).</li>
<li>And let&#8217;s get rid of the function call which, take it from me, in this case is effectively a single table outer join lookup anyway.</li>
</ul>
<p><pre class="brush: plain;">
SELECT  /*+
          find_me
          monitor
          qb_name(main)
          dynamic_sampling(sdol@main 4)
          dynamic_sampling(isin@main 4)
          */
        ... some columns ...
FROM   (SELECT /*+
                 qb_name(union1)
                 opt_estimate(join(isbk@union1 rule@union1) scale_rows=100)
                 */
               isbk.inst_num  inst_num
        ,      inix.inst_code nix_code
        ,      (SELECT book.name
                FROM   book
                WHERE  book.book_num = rule.book_num) book_name
        FROM   isbk
        ,      rule
        ,      inix
        WHERE  rule.rule_type_code    = 'IBK'
        AND    rule.book_num          = isbk.book_num 
        AND    inix.inst_num      (+) = isbk.inst_num
        AND    inix.xcod_code     (+) = 'NIX'
        UNION ALL
        SELECT /*+
                 qb_name(union2)
                 dynamic_sampling(inix@union2 4)
                 */
               inix.inst_num  inst_num
        ,      inix.inst_code nix_code
        ,      NULL           book_name
        FROM   inix
        WHERE  inix.xcod_code         = 'NIX'
        AND    NOT EXISTS (SELECT /*+ 
                                    qb_name(sub1)
                                    opt_estimate(join(isbk@sub1 rule@sub1) scale_rows=100)
                                    */
                                  1
                           FROM   isbk
                           ,      rule
                           WHERE  rule.rule_type_code    = 'IBK'
                           AND    rule.book_num          = isbk.book_num
                           AND    isbk.inst_num         = inix.inst_num))
       xxxx
,      bsta
,      isin
,      sdol
,      inst
,      borg
,      isdm
WHERE  bsta.inst_num          = xxxx.inst_num
AND    bsta.current_mat_date &gt;= TO_DATE(:v_bus_date, 'yyyymmdd')
AND    isin.inst_num          = xxxx.inst_num 
AND    isin.xcod_code         = 'SEDL'
AND    sdol.inst_num          = xxxx.inst_num
AND    sdol.xcod_code         = 'ISIN'
AND    inst.inst_num          = xxxx.inst_num
AND    borg.borg_num          = inst.issuer_num
AND    isdm.inst_num          = xxxx.inst_num;
</pre></p>
<p>Which gives:</p>
<pre>
<font size="-1">
Global Stats
=================================================
| Elapsed |   Cpu   |  Other   | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls |  Gets  |
=================================================
|    1.40 |    1.40 |     0.00 |    60 |  79273 |
=================================================

SQL Plan Monitoring Details (Plan Hash Value=2392527494)
=======================================================================================================================
| Id |               Operation                |   Name      |  Rows   | Execs |   Rows   | Activity | Activity Detail |
|    |                                        |             | (Estim) |       | (Actual) |   (%)    |   (# samples)   |
=======================================================================================================================
|  0 | SELECT STATEMENT                       |             |         |     1 |    29225 |          |                 |
|  1 |   HASH JOIN                            |             |   94337 |     1 |    29225 |          |                 |
|  2 |    INDEX RANGE SCAN                    | ISCO_4_IDX  |    373K |     1 |     377K |          |                 |
|  3 |    HASH JOIN                           |             |   80747 |     1 |    29629 |          |                 |
|  4 |     HASH JOIN                          |             |   80747 |     1 |    29629 |          |                 |
|  5 |      HASH JOIN                         |             |   80747 |     1 |    29629 |          |                 |
|  6 |       HASH JOIN                        |             |   78122 |     1 |    29604 |          |                 |
|  7 |        MERGE JOIN                      |             |   78003 |     1 |    33947 |          |                 |
|  8 |         INDEX RANGE SCAN               | ISCO_4_IDX  |    818K |     1 |     861K |          |                 |
|  9 |         SORT JOIN                      |             |   55765 |  861K |    33947 |          |                 |
| 10 |          VIEW                          |             |   55765 |     1 |    33960 |          |                 |
| 11 |           UNION-ALL                    |             |         |     1 |    33960 |          |                 |
| 12 |            TABLE ACCESS BY INDEX ROWID | BOOK        |       1 |     1 |        1 |          |                 |
| 13 |             INDEX UNIQUE SCAN          | BOOK_IDX    |       1 |     1 |        1 |          |                 |
| 14 |            HASH JOIN OUTER             |             |   55485 |     1 |    31674 |          |                 |
| 15 |             NESTED LOOPS               |             |   55485 |     1 |    31674 |          |                 |
| 16 |              INDEX RANGE SCAN          | RULE_IDX_2  |       3 |     1 |        1 |          |                 |
| 17 |              INDEX RANGE SCAN          | ISBK_IDX    |   17609 |     1 |    31674 |          |                 |
| 18 |             INDEX RANGE SCAN           | ISCO_4_IDX  |    170K |     1 |    32983 |          |                 |
| 19 |            HASH JOIN ANTI              |             |     280 |     1 |     2286 |          |                 |
| 20 |             INDEX RANGE SCAN           | ISCO_4_IDX  |   28028 |     1 |    32983 |          |                 |
| 21 |             VIEW                       | VW_SQ_1     |   55485 |     1 |    31674 |          |                 |
| 22 |              NESTED LOOPS              |             |   55485 |     1 |    31674 |          |                 |
| 23 |               INDEX RANGE SCAN         | RULE_IDX_2  |       3 |     1 |        1 |          |                 |
| 24 |               INDEX RANGE SCAN         | ISBK_IDX    |   17609 |     1 |    31674 |          |                 |
| 25 |        TABLE ACCESS FULL               | BSTA        |    144K |     1 |     266K |          |                 |
| 26 |       TABLE ACCESS FULL                | ISDM        |      1M |     1 |       1M |          |                 |
| 27 |      TABLE ACCESS FULL                 | INST        |    796K |     1 |     796K |   100.00 | Cpu (1)         |
| 28 |     TABLE ACCESS FULL                  | BORG        |    328K |     1 |     328K |          |                 |
=======================================================================================================================
</font>
</pre>
<p>It might not be perfect but it&#8217;s certainly a lot better.</p>
<p>An acceptable compromise might be to refactor the UNION ALL as per above but omit the undocumented opt_estimate hints.</p>
<p>All we need to do is some more testing to validate the results and to also triple check the performance when the data is not cached and we&#8217;re done.</p>
<p>If you made it this far then Wow! I probably wouldn&#8217;t have done.</p>
<p>So, to summarise, what have we done?</p>
<ul>
<li>Well, we haven&#8217;t needed to know much about the original intention of the query nor the schema.</li>
<li>We&#8217;ve improved the accuracies of some of the estimates.</li>
<li>We&#8217;ve found some redundant tables and joins.</li>
<li>And we&#8217;ve reordered the query slightly to better phrase the question that we think was being asked, in the process moving the UNION ALL earlier in the processing so that some of the joining tables only needed to be referenced once (an advantage that might not be preserved if the CBO decides to merge the UNION ALL but that would be the opposite of the recently developed join factorisation mechanism).</li>
</ul>
<p>And these simple, quick steps effectively reduced the execution time of a query from 20 minutes to a few seconds.</p>
<p>In hindsight, a good example because of the gains realised but a bad example because of the length of query and the associated detail, particularly the redundant tables.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/orastory.wordpress.com/1339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/orastory.wordpress.com/1339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/orastory.wordpress.com/1339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/orastory.wordpress.com/1339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/orastory.wordpress.com/1339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/orastory.wordpress.com/1339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/orastory.wordpress.com/1339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/orastory.wordpress.com/1339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/orastory.wordpress.com/1339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/orastory.wordpress.com/1339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/orastory.wordpress.com/1339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/orastory.wordpress.com/1339/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/orastory.wordpress.com/1339/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/orastory.wordpress.com/1339/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1339&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://orastory.wordpress.com/2012/01/11/sql-tuning-request/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">dombrooks</media:title>
		</media:content>
	</item>
		<item>
		<title>Scalar Subselect Costing</title>
		<link>http://orastory.wordpress.com/2012/01/09/scalar-subselect-cost-accounting/</link>
		<comments>http://orastory.wordpress.com/2012/01/09/scalar-subselect-cost-accounting/#comments</comments>
		<pubDate>Mon, 09 Jan 2012 10:19:31 +0000</pubDate>
		<dc:creator>Dom Brooks</dc:creator>
				<category><![CDATA[11g]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[cbo]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[execution plan]]></category>
		<category><![CDATA[explain plan]]></category>
		<category><![CDATA[oracle]]></category>

		<guid isPermaLink="false">http://orastory.wordpress.com/?p=1341</guid>
		<description><![CDATA[This issue is an oldie but deserving of a quick post to stop me going off on a tangent in another post. It is an oddity of scalar subselects/subqueries that their cost is not taken into account in the top level cost of a query. In older versions of Oracle, it used to be the [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1341&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>This issue is an oldie but deserving of a quick post to stop me going off on a tangent in another post.</p>
<p>It is an oddity of scalar subselects/subqueries that their cost is not taken into account in the top level cost of a query.</p>
<p>In older versions of Oracle, it used to be the case that you didn&#8217;t even see the scalar subquery in the execution plan.</p>
<p>However, even in the latest versions, the cost still isn&#8217;t accounted for.</p>
<p>Always something to keep in mind.</p>
<p>For example:</p>
<p><pre class="brush: plain;">
SQL&gt; create table t1
  2  (col1 number not null);

Table created.

SQL&gt; 
SQL&gt; insert into t1
  2  select rownum
  3  from   dual
  4  connect by rownum &lt;= 10000;

10000 rows created.

SQL&gt; 
SQL&gt; commit;

Commit complete.

SQL&gt; 
SQL&gt; create table t2
  2  (col1 number not null primary key);

Table created.

SQL&gt; 
SQL&gt; 
SQL&gt; insert into t2
  2  select rownum
  3  from   dual
  4  connect by rownum &lt;= 10000;

10000 rows created.

SQL&gt; 
SQL&gt; commit;

Commit complete.

SQL&gt; 
</pre></p>
<p>Let&#8217;s do a scalar subselect to do an index lookup on t2 for every row in t1:</p>
<p><pre class="brush: plain;">
SQL&gt; explain plan for
  2  select t1.col1
  3  ,      (select t2.col1 from t2 where t2.col1 = t1.col1)
  4  from   t1;

Explained.

SQL&gt; 
SQL&gt; select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
Plan hash value: 2339000913

----------------------------------------------------------------------------------
| Id  | Operation         | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |              | 10000 |   126K|     8   (0)| 00:00:01 |
|*  1 |  INDEX UNIQUE SCAN| SYS_C0078310 |     1 |    13 |     1   (0)| 00:00:01 |
|   2 |  TABLE ACCESS FULL| T1           | 10000 |   126K|     8   (0)| 00:00:01 |
----------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access(&quot;T2&quot;.&quot;COL1&quot;=:B1)

</pre></p>
<p>You can see that the cost of the scalar subquery is 1 per execution and it&#8217;s not accounted for at the top level.</p>
<p>Let&#8217;s force a full table scan of the row-by-row lookup:</p>
<p><pre class="brush: plain;">
SQL&gt; explain plan for
  2  select t1.col1
  3  ,      (select /*+ full(t2) */ t2.col1 from t2 where t2.col1 = t1.col1)
  4  from   t1;

Explained.

SQL&gt; 
SQL&gt; select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------
Plan hash value: 637946564

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 10000 |   126K|     8   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T2   |     1 |    13 |     2   (0)| 00:00:01 |
|   2 |  TABLE ACCESS FULL| T1   | 10000 |   126K|     8   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(&quot;T2&quot;.&quot;COL1&quot;=:B1)

</pre></p>
<p>Obviously a much more expensive operation but, again, not properly accounted for in the overall costing.</p>
<p>Wouldn&#8217;t it be preferable that as the optimizer has estimated the number of rows in the top level select:<br />
<pre class="brush: plain;">
|   0 | SELECT STATEMENT  |      | 10000 |   126K|     8   (0)| 00:00:01 |
</pre><br />
and it has estimated the cost per execution of the scalar subselect:<br />
<pre class="brush: plain;">
|*  1 |  TABLE ACCESS FULL| T2   |     1 |    13 |     2   (0)| 00:00:01 |
</pre></p>
<p>that the top level cost include to some degree the cost of scalar subselect per execution * estimated executions?</p>
<p>For example, if we code a join roughly equivalent to the scalar subselect then:</p>
<p><pre class="brush: plain;">
SQL&gt; explain plan for
  2  select /*+ 
  3           full(t2) 
  4           use_nl(t2)
  5           */
  6         t1.col1
  7  ,      t2.col1
  8  from   t1
  9  ,      t2
 10  where t2.col1 (+) = t1.col1;

Explained.

SQL&gt; 
SQL&gt; select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------
Plan hash value: 2453408398

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      | 10000 |   253K| 66919   (7)| 00:05:35 |
|   1 |  NESTED LOOPS OUTER|      | 10000 |   253K| 66919   (7)| 00:05:35 |
|   2 |   TABLE ACCESS FULL| T1   | 10000 |   126K|     8   (0)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL| T2   |     1 |    13 |     7  (15)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter(&quot;T2&quot;.&quot;COL1&quot;(+)=&quot;T1&quot;.&quot;COL1&quot;)

</pre></p>
<p>Also see:<br />
<a href="http://jonathanlewis.wordpress.com/2007/10/12/scalar-subqueries/" title="http://jonathanlewis.wordpress.com/2007/10/12/scalar-subqueries/">http://jonathanlewis.wordpress.com/2007/10/12/scalar-subqueries/</a></p>
<p><a href="http://oracle-randolf.blogspot.com/2010/01/when-your-projection-is-not-cost-free.html" title="http://oracle-randolf.blogspot.com/2010/01/when-your-projection-is-not-cost-free.html">http://oracle-randolf.blogspot.com/2010/01/when-your-projection-is-not-cost-free.html</a></p>
<p><a href="http://blog.sydoracle.com/2005/09/explain-plans-and-scalar-subqueries.html" title="http://blog.sydoracle.com/2005/09/explain-plans-and-scalar-subqueries.html">http://blog.sydoracle.com/2005/09/explain-plans-and-scalar-subqueries.html</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/orastory.wordpress.com/1341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/orastory.wordpress.com/1341/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/orastory.wordpress.com/1341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/orastory.wordpress.com/1341/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/orastory.wordpress.com/1341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/orastory.wordpress.com/1341/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/orastory.wordpress.com/1341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/orastory.wordpress.com/1341/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/orastory.wordpress.com/1341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/orastory.wordpress.com/1341/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/orastory.wordpress.com/1341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/orastory.wordpress.com/1341/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/orastory.wordpress.com/1341/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/orastory.wordpress.com/1341/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1341&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://orastory.wordpress.com/2012/01/09/scalar-subselect-cost-accounting/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">dombrooks</media:title>
		</media:content>
	</item>
		<item>
		<title>Hints of Acceptability</title>
		<link>http://orastory.wordpress.com/2011/12/12/hints-of-acceptability/</link>
		<comments>http://orastory.wordpress.com/2011/12/12/hints-of-acceptability/#comments</comments>
		<pubDate>Mon, 12 Dec 2011 21:48:42 +0000</pubDate>
		<dc:creator>Dom Brooks</dc:creator>
				<category><![CDATA[11g]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[hints]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[sql tuning]]></category>

		<guid isPermaLink="false">http://orastory.wordpress.com/?p=1323</guid>
		<description><![CDATA[There are hints and then there are hints. In version 11.2.0.3 there are 273 hints listed in V$SQL_HINT. That&#8217;s four more than 11.2.0.2 by the way &#8211; (NO_)FULL_OUTER_JOIN_TO_OUTER and (NO_)OUTER_JOIN_TO_ANTI are the new additions. But V$SQL_HINT doesn&#8217;t seem to be an absolutely comprehensive listing. I only noticed one interesting omission &#8211; there&#8217;s no entry for [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1323&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>There are hints and then there are hints.</p>
<p>In version 11.2.0.3 there are 273 hints listed in V$SQL_HINT.</p>
<p>That&#8217;s four more than 11.2.0.2 by the way &#8211; (NO_)FULL_OUTER_JOIN_TO_OUTER and (NO_)OUTER_JOIN_TO_ANTI are the new additions.</p>
<p>But V$SQL_HINT doesn&#8217;t seem to be an absolutely comprehensive listing.</p>
<p>I only noticed one interesting omission &#8211; there&#8217;s no entry for PARALLEL.</p>
<p>There are entries for NO_PARALLEL / NOPARALLEL but these list their INVERSE as SHARED not PARALLEL.</p>
<p>I&#8217;ve never used or even heard of the SHARED hint but it certainly seems to just be synonymous with PARALLEL. Of course, the documentation documents PARALLEL but makes no mention of SHARED which has been a valid alternative since 8.1.0.</p>
<p>So, going down the entries in V$SQL_HINT, below is my initial attempt at a list of &#8220;hints of acceptability&#8221;, even if one or two are undocumented.</p>
<p>As long as their usage is appropriate, I think these can be used pretty much without guilt or sense of defeat / failure.</p>
<p><strong>Those related to optimizer mode:</strong></p>
<ul>
<li>
<a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#BABHIAIG">ALL_ROWS</a></li>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#BABGCCFG">FIRST_ROWS</a></li>
</ul>
<p><strong>Those related to direct path operations:</strong></p>
<ul>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#BABBBHCJ">APPEND</a></li>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#BABFIHGA">APPEND_VALUES</a></li>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#BABJCJFG">NOAPPEND</a></li>
</ul>
<p><strong>Those related to optimizer cardinality/selectivity estimate adjustments:</strong></p>
<ul>
<li>CARDINALITY (Undocumented)</li>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#BABDCGAA">DYNAMIC_SAMPLING</a> (In recent years, this has been my most favorite hint) </li>
<li>DYNAMIC_SAMPLING_EST_CDN (Undocumented since 9i)</li>
<li>OPT_ESTIMATE (Undocumented but <a href="http://www.pythian.com/news/13469/oracles-opt_estimate-hint-usage-guide/">useful link</a>)</li>
</ul>
<p><strong>Those normally related to bugs and associated parameter changes and fix control:</strong></p>
<ul>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#BABEBAID">OPT_PARAM</a> (documentation is inaccurate when it says that it&#8217;s only valid for 5 parameters) </li>
<li>OPTIMIZER_FEATURES_ENABLE (undocumented)</li>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#BABGDAGI">NO_QUERY_TRANSFORMATION</a> </li>
</ul>
<p><strong>Those related to bind variable/literal usage:</strong></p>
<ul>
<li>
<a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#autoId10">CURSOR_SHARING_EXACT</a> (never had to use it myself but I can see how it might be useful) </li>
</ul>
<p><strong>Those related to parallel operations:</strong></p>
<ul>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#autoId63">PARALLEL / SHARED</a> </li>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#autoId64">PARALLEL_INDEX</a> </li>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#autoId42">NO_PARALLEL / NOPARALLEL</a> </li>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#autoId44">NO_PARALLEL_INDEX / NOPARALLEL_INDEX</a> </li>
</ul>
<p><strong>Those related to remote operations:</strong></p>
<ul>
<li>
<a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#autoId11">DRIVING_SITE</a> </li>
</ul>
<p><strong>Those related to real time sql monitoring:</strong></p>
<ul>
<li>
<a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#autoId30">MONITOR</a></li>
</ul>
<p><strong>Those related to tuning but which should not make it into production code:</strong></p>
<ul>
<li><a href="http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_xplan.htm">GATHER_PLAN_STATISTICS</a> (indirectly documented) </li>
</ul>
<p><strong>Those related to caching and caching-like behaviours:</strong></p>
<ul>
<li>MATERIALIZE (undocumented)</li>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#autoId70">RESULT_CACHE</a> </li>
</ul>
<p><strong>Those related to query block naming:</strong></p>
<ul>
<li><a href="http://docs.oracle.com/cd/E11882_01/server.112/e26088/sql_elements006.htm#BABJIFFH">QB_NAME</a> </li>
</ul>
<p>Have I missed any obvious candidates?<br />
Is there anything you would add?</p>
<p>Of these listed above, let&#8217;s just dwell very briefly on those related to optimizer estimate adjustments.</p>
<p>Relatively speaking, do you not find that most &#8211; most not all &#8211; issues regarding SQL performance are related to accuracy &#8211; or rather inaccuracy &#8211; of rowsource estimates? </p>
<p>If so, then recommended reading should be <a href="http://www.centrexcc.com/Tuning%20by%20Cardinality%20Feedback.pdf">Wolfgang Breitling&#8217;s Tuning by Cardinality Feeback</a>, the bases of which are:</p>
<ol>
<li>The observation that:<br />
<blockquote><p><strong>IF AN ACCESS PLAN IS NOT OPTIMAL IT IS BECAUSE THE CARDINALITY ESTIMATE FOR ONE OR MORE OF THE ROW SOURCES IS GROSSLY INCORRECT.</strong></p></blockquote>
</li>
<li>The conjecture that:<br />
<strong></p>
<blockquote><p>THE CBO DOES AN EXCELLENT JOB OF FINDING THE BEST ACCESS PLAN FOR A GIVEN SQL PROVIDED IT IS ABLE TO ACCURATELY ESTIMATE THE CARDINALITIES OF THE ROW SOURCES IN THE PLAN.</p></blockquote>
<p></strong>
</li>
</ol>
<p>If the scope of a problem is one or two SQL statements, then a solution with a scope limited to one or two SQL statements &#8211; i.e. a rewrite or a hint &#8211; is more appropriate than something with a wider scope such as changing tab/column stats and/or histograms.</p>
<p>And in this respect a solution forcing an estimate adjustment &#8211; whether by a hard number by CARDINALITY or OPT_ESTIMATE, an adjustment fudge factor also via OPT_ESTIMATE or having a peek at some of the data in question via DYNAMIC_SAMPLING (only good for single table predicates) is more often than not a better, more flexible, longer lasting solution than forcing a nested loop or a hash join or a particular index.</p>
<p>There are, of course, times when you have no option &#8211; there are reasons why all these hundreds of hints exist after all. </p>
<p>But I always think that if I can&#8217;t get what I think I roughly want &#8211; and what I normally want is just for the estimates to be broadly accurate &#8211; either by rewriting the SQL or by using one of these acceptable hints then it&#8217;s almost an admission failure.</p>
<p>Away from the list above, hinting a SQL statement is not something which should be undertaken lightly.</p>
<p>Do you ever see a lot of sql statements joining at least a handful of tables but with a single USE_NL hint here or an INDEX hint there in the belief that this offers some sort of stability for the woolly concept of &#8220;the correct plan&#8221;? I know I do.</p>
<p>Bottom line: if you can avoid hinting you absolutely should.</p>
<p>But if you really are going to hint, you should be doing it properly.</p>
<p>What does this mean?:</p>
<ol>
<li>Being prescriptive and unambiguous with your directives &#8211; i.e. a single use_nl hint is not sufficient for a sql statement that joins eight tables for example.</li>
<li>Using the full specification of the hint including queryblock and table specification syntax.</li>
</ol>
<p>For more on queryblock naming see Jonathan Lewis&#8217;s <a href="http://jonathanlewis.wordpress.com/2007/06/25/qb_name/">article on qb_name including the discussions in the comments</a>.</p>
<p>For more information on what you have to do to properly hint, see <a href="http://jonathanlewis.wordpress.com/2011/06/08/how-to-hint-1/">this excellent article on by Jonathan Lewis</a>.</p>
<p>If Jonathan&#8217;s &#8220;simple&#8221; illustration is not enough to seriously make you reconsider your addiction to hinting, then you have issues and you have to be prepared to swallow the full specification including both query block and proper table specifications.</p>
<p>Yes, it&#8217;s ugly.</p>
<p>Yes, it&#8217;s not easy (compared to how you&#8217;ve probably been doing it).</p>
<p>But, if you&#8217;re thinking the above, perhaps revisit your attitude to hinting.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/orastory.wordpress.com/1323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/orastory.wordpress.com/1323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/orastory.wordpress.com/1323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/orastory.wordpress.com/1323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/orastory.wordpress.com/1323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/orastory.wordpress.com/1323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/orastory.wordpress.com/1323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/orastory.wordpress.com/1323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/orastory.wordpress.com/1323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/orastory.wordpress.com/1323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/orastory.wordpress.com/1323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/orastory.wordpress.com/1323/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/orastory.wordpress.com/1323/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/orastory.wordpress.com/1323/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1323&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://orastory.wordpress.com/2011/12/12/hints-of-acceptability/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">dombrooks</media:title>
		</media:content>
	</item>
		<item>
		<title>Projection Pushdown bug in 11.2.0.3</title>
		<link>http://orastory.wordpress.com/2011/12/08/projection-pushdown-bug-in-11-2-0-3/</link>
		<comments>http://orastory.wordpress.com/2011/12/08/projection-pushdown-bug-in-11-2-0-3/#comments</comments>
		<pubDate>Thu, 08 Dec 2011 13:23:29 +0000</pubDate>
		<dc:creator>Dom Brooks</dc:creator>
				<category><![CDATA[11gR2]]></category>
		<category><![CDATA[optimizer]]></category>
		<category><![CDATA[oracle]]></category>

		<guid isPermaLink="false">http://orastory.wordpress.com/?p=1309</guid>
		<description><![CDATA[There&#8217;s a bug in 11.2.0.3 related to projection pushdown that seems to be influenced by the setting of STATISTICS_LEVEL. Having taken the time to distill a test case from a real world issue (commenting out columns and logic and bringing view definitions inline etc &#8211; original query had no reference to DUAL), turns out it&#8217;s [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1309&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>There&#8217;s a bug in 11.2.0.3 related to projection pushdown that seems to be influenced by the setting of <a href="http://docs.oracle.com/cd/E11882_01/server.112/e25513/initparams250.htm">STATISTICS_LEVEL</a>.</p>
<p>Having taken the time to distill a test case from a real world issue (commenting out columns and logic and bringing view definitions inline etc &#8211; original query had no reference to DUAL), turns out it&#8217;s very simple to reproduce:</p>
<p><pre class="brush: plain;">
SQL&gt; SELECT * FROM v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE    11.2.0.3.0      Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

SQL&gt; ALTER SESSION SET STATISTICS_LEVEL=ALL;

Session altered.

SQL&gt; SELECT 
  2         1     col1,
  3         'X'   col2
  4  FROM   DUAL  trad
  5  UNION
  6  SELECT (SELECT SUM(1)
  7          FROM   DUAL) col1,
  8         TO_CHAR (NULL) col2
  9  FROM   DUAL ;
FROM   DUAL  trad
       *
ERROR at line 4:
ORA-00600: internal error code, arguments: [qkeIsExprReferenced1], [], [], [],
[], [], [], [], [], [], [], []


SQL&gt; 
</pre></p>
<p>Maybe you&#8217;re thinking that you probably don&#8217;t tend to run with STATISTICS_LEVEL set to ALL though?</p>
<p>But perhaps you&#8217;re used to doing your SQL tuning with the GATHER_PLAN_STATISTICS hint?</p>
<p><pre class="brush: plain;">
SQL&gt; SELECT /*+ gather_plan_statistics */
  2         1     col1,
  3         'X'   col2
  4  FROM   DUAL  trad
  5  UNION
  6  SELECT (SELECT SUM(1)
  7          FROM   DUAL) col1,
  8         TO_CHAR (NULL) col2
  9  FROM   DUAL ;
FROM   DUAL  trad
       *
ERROR at line 4:
ORA-00600: internal error code, arguments: [qkeIsExprReferenced1], [], [], [],
[], [], [], [], [], [], [], []


SQL&gt; 
</pre></p>
<p>You can address the bug easily enough by altering _projection_pushdown to false like so:<br />
<pre class="brush: plain;">
SQL&gt; ALTER SESSION SET &quot;_projection_pushdown&quot; = false;

Session altered.

SQL&gt; SELECT /*+ gather_plan_statistics */
  2         1     col1,
  3         'X'   col2
  4  FROM   DUAL  trad
  5  UNION
  6  SELECT (SELECT SUM(1)
  7          FROM   DUAL) col1,
  8         TO_CHAR (NULL) col2
  9  FROM   DUAL ;

      COL1 C
---------- -
         1 X
         1

SQL&gt; ALTER SESSION SET STATISTICS_LEVEL=ALL;

Session altered.

SQL&gt; SELECT 1     col1,
  2         'X'   col2
  3  FROM   DUAL  trad
  4  UNION
  5  SELECT (SELECT SUM(1)
  6          FROM   DUAL) col1,
  7         TO_CHAR (NULL) col2
  8  FROM   DUAL ;

      COL1 C
---------- -
         1 X
         1

SQL&gt; 
</pre></p>
<p>Unfortunately, if you&#8217;re turning projection pushdown off, then that must critically change the plan of the statement you&#8217;re looking at.</p>
<p>Any thoughts that you might use the column projection information of DBMS_XPLAN to help diagnose the bug are scuppered by the fact that this is obviously raised at parse time.</p>
<p>What is particularly disappointing is that we found this on Day 1 of testing an application against 11.2.0.3, an application which we&#8217;ve previously tested extensively on 11.2.0.2 and were considering 11.2.0.3 rather than request individual backports for a couple of specific, unrelated bugs that we&#8217;ve yet to address on 11.2.0.2.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/orastory.wordpress.com/1309/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/orastory.wordpress.com/1309/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/orastory.wordpress.com/1309/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/orastory.wordpress.com/1309/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/orastory.wordpress.com/1309/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/orastory.wordpress.com/1309/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/orastory.wordpress.com/1309/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/orastory.wordpress.com/1309/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/orastory.wordpress.com/1309/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/orastory.wordpress.com/1309/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/orastory.wordpress.com/1309/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/orastory.wordpress.com/1309/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/orastory.wordpress.com/1309/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/orastory.wordpress.com/1309/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1309&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://orastory.wordpress.com/2011/12/08/projection-pushdown-bug-in-11-2-0-3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">dombrooks</media:title>
		</media:content>
	</item>
		<item>
		<title>WFG &#8211; mode 5 TM deadlock</title>
		<link>http://orastory.wordpress.com/2011/11/23/wfg-mode-5-tm-deadlock/</link>
		<comments>http://orastory.wordpress.com/2011/11/23/wfg-mode-5-tm-deadlock/#comments</comments>
		<pubDate>Wed, 23 Nov 2011 12:06:41 +0000</pubDate>
		<dc:creator>Dom Brooks</dc:creator>
				<category><![CDATA[deadlock]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[rac]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://orastory.wordpress.com/?p=1303</guid>
		<description><![CDATA[Just a brief note to preserve some observations on a deadlock situation reported a while back on the OTN forums. The symptoms of the problem were deadlocks on a RAC system and this example partial extract of a wait-for-graph from an LMD trace file: The details of a WFG are slightly different compared to a [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1303&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Just a brief note to preserve some observations on a deadlock situation reported a while back on the <a href="https://forums.oracle.com/forums/thread.jspa?threadID=2310862">OTN forums</a>.</p>
<p>The symptoms of the problem were deadlocks on a RAC system and this example partial extract of a wait-for-graph from an LMD trace file:<br />
<pre class="brush: plain;">
Global Wait-For-Graph(WFG) at ddTS[0.5] :
BLOCKED 0x3d2438198 4 wq 2 cvtops x1 TM 0x1df87.0x0(ext 0x0,0x0)[41000-0001-000004A5] inst 1 
BLOCKER 0x3d7846108 4 wq 2 cvtops x1 TM 0x1df87.0x0(ext 0x0,0x0)[38000-0002-00002C68] inst 2 
BLOCKED 0x3d7846108 4 wq 2 cvtops x1 TM 0x1df87.0x0(ext 0x0,0x0)[38000-0002-00002C68] inst 2 
BLOCKER 0x3d75a4fd8 4 wq 2 cvtops x1 TM 0x1df87.0x0(ext 0x0,0x0)[43000-0003-000009C1] inst 3 
BLOCKED 0x3d75a4fd8 4 wq 2 cvtops x1 TM 0x1df87.0x0(ext 0x0,0x0)[43000-0003-000009C1] inst 3 
BLOCKER 0x3d2438198 4 wq 2 cvtops x1 TM 0x1df87.0x0(ext 0x0,0x0)[41000-0001-000004A5] inst 1 
</pre></p>
<p>The details of a WFG are slightly different compared to a non-RAC/LMD deadlock graph, not least how to interpret lock mode.</p>
<p>Column 8 indicates a TM lock on the object in column 9 &#8211; 0x1df87 (hex that can be converted to dec and used in dba_objects)</p>
<p>The lock mode in column 3 is slightly misleading in that &#8220;4&#8243; does not mean mode 4 but mode 5, thanks to the following resources:</p>
<ul>
<li><a href="http://jonathanlewis.wordpress.com/2010/06/21/locks/">Jonathan Lewis: Lock Modes</a></li>
<li><a href="http://www.rachelp.nl/index_kb.php?menu=articles&amp;actie=show&amp;id=15">An article on the RACHELP.NL website</a></li>
</ul>
<p>and this:<br />
<pre class="brush: plain;">
#define KJUSERNL 0         /* no permissions */   (Null)
#define KJUSERCR 1         /* concurrent read */ (Row-S (SS))
#define KJUSERCW 2         /* concurrent write */ (Row-X (SX))
#define KJUSERPR 3         /* protected read */    (Share)
#define KJUSERPW 4         /* protected write */ (S/Row-X (SSX))
#define KJUSEREX 5         /* exclusive access */ (Exclusive)
</pre></p>
<p>&#8220;wq 2&#8243; is apparently the convert queue, &#8220;wq 1&#8243; being the grant queue.</p>
<p>So, in summary, deadlock issues regarding mode 5 TM locks.</p>
<p>In this particular case, the usual culprit was an unindexed foreign key, albeit a less common variation with a self-referencing foreign key in conjunction with updates to the primary key column &#8211; a questionable habit which in my mind then raises questions about the design.</p>
<p>E.g.<br />
<pre class="brush: plain;">
Session 1&gt;create table t1
  2  (col1         number       not null
  3  ,col2         varchar2(10) not null
  4  ,related_col1 number
  5  ,constraint pk_t1 primary key (col1)
  6  ,constraint fk_t1 foreign key (related_col1) references t1 (col1));

Table created.

Session 1&gt;insert into t1 values (1,'X',null);

1 row created.

Session 1&gt;insert into t1 values (2,'X',null);

1 row created.

Session 1&gt;commit;

Commit complete.

Session 1&gt;
</pre></p>
<p>If we try to update the pk then session 2 blocks:</p>
<p>Session 1:<br />
<pre class="brush: plain;">
Session 1&gt;update t1 set col1 = 1  where col1 = 1;

1 row updated.

Session 1&gt;
</pre></p>
<p>Session 2:<br />
<pre class="brush: plain;">
Session 2&gt;update t1 set col1 = 2 where col1 = 2;
... hangs...
</pre></p>
<p>Blocking on a mode 5 TM:<br />
<pre class="brush: plain;">
Session 1&gt;select process,
  2         l.sid,
  3         type,
  4         lmode,
  5         request,
  6         do.object_name
  7  from   v$locked_object lo,
  8         dba_objects do,
  9         v$lock l
 10  where  lo.object_id   = do.object_id
 11  AND    l.sid          = lo.session_id
 12  AND    do.object_name IN ('T1');

PROCESS           SID TY      LMODE    REQUEST OBJECT_NAME
---------- ---------- -- ---------- ---------- -----------
6052:1248        1010 AE          4          0 T1
6052:1248        1010 TM          0          5 T1
496:4000         1640 AE          4          0 T1
496:4000         1640 TM          3          0 T1
496:4000         1640 TX          6          0 T1

Session 1&gt;
</pre></p>
<p>It&#8217;s not hard to go and engineer a deadlock scenario but as it would be so artificial, I won&#8217;t bother.</p>
<p>And as expected, an index addresses the issue.</p>
<p>Session 1:<br />
<pre class="brush: plain;">
Session 1&gt;rollback;

Rollback complete.

Session 1&gt;create index i_t1 on t1 (related_col1);

Index created.

Session 1&gt;update t1 set col1 = 1  where col1 = 1;

1 row updated.

Session 1&gt;
</pre></p>
<p>Session 2:<br />
<pre class="brush: plain;">
Session 2&gt;rollback;

Rollback complete.

Session 2&gt;update t1 set col1 = 2 where col1 = 2;

1 row updated.

Session 2&gt;
</pre></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/orastory.wordpress.com/1303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/orastory.wordpress.com/1303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/orastory.wordpress.com/1303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/orastory.wordpress.com/1303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/orastory.wordpress.com/1303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/orastory.wordpress.com/1303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/orastory.wordpress.com/1303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/orastory.wordpress.com/1303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/orastory.wordpress.com/1303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/orastory.wordpress.com/1303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/orastory.wordpress.com/1303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/orastory.wordpress.com/1303/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/orastory.wordpress.com/1303/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/orastory.wordpress.com/1303/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1303&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://orastory.wordpress.com/2011/11/23/wfg-mode-5-tm-deadlock/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">dombrooks</media:title>
		</media:content>
	</item>
		<item>
		<title>What job runs AWR snapshot? Eh?</title>
		<link>http://orastory.wordpress.com/2011/10/27/what-job-runs-awr-snapshot/</link>
		<comments>http://orastory.wordpress.com/2011/10/27/what-job-runs-awr-snapshot/#comments</comments>
		<pubDate>Thu, 27 Oct 2011 13:09:57 +0000</pubDate>
		<dc:creator>Dom Brooks</dc:creator>
				<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11g]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[awr]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[mmon]]></category>

		<guid isPermaLink="false">http://orastory.wordpress.com/?p=1277</guid>
		<description><![CDATA[There are a surprising number of online resources propagating the idea that the collecting of AWR snapshots is somehow connected to the GATHER_STATS_JOB (which was responsible for automatically gathering stats in 10g). Eh? Collecting AWR snapshots is one of the responsibilities of the background process MMON. If you have a problem with the automatic snapshots: [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1277&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>There are <a href="http://www.lmgtfy.com/?q=oracle+what+job+runs+awr+snapshot%3F">a surprising number of online resources</a> propagating the idea that the collecting of AWR snapshots is somehow connected to the <a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14211/stats.htm#sthref1068">GATHER_STATS_JOB</a> (which was responsible for automatically gathering stats in 10g).</p>
<p>Eh?</p>
<p>Collecting AWR snapshots is one of the <strong><a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e25789/cncptdba.htm#autoId26">responsibilities of the background process MMON</a></strong>.</p>
<p>If you have a problem with the automatic snapshots:<br />
- Check whether you can manually snap using <a href="http://download.oracle.com/docs/cd/E11882_01/appdev.112/e25788/d_workload_repos.htm#autoId24">DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT</a>.<br />
- Check MMON and alert log / trace files for related messages.<br />
- See Metalink note 1301503.1: &#8220;Troubleshooting: AWR Snapshot Collection issues&#8221;</p>
<p>This came up in a question on the OTN forums where the OP said that AWR was not snapping automatically but the GATHER_STATS_JOB seemed to be ok. And the obvious first question would be where did you get that idea from?<br />
Sometimes you can&#8217;t win on the forums. If you ask a question, it&#8217;s not uncommon to be pointed to a google search. But if you google something, you can&#8217;t trust everything that&#8217;s out there &#8211; it gets outdated and sometimes it never was accurate.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/orastory.wordpress.com/1277/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/orastory.wordpress.com/1277/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/orastory.wordpress.com/1277/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/orastory.wordpress.com/1277/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/orastory.wordpress.com/1277/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/orastory.wordpress.com/1277/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/orastory.wordpress.com/1277/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/orastory.wordpress.com/1277/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/orastory.wordpress.com/1277/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/orastory.wordpress.com/1277/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/orastory.wordpress.com/1277/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/orastory.wordpress.com/1277/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/orastory.wordpress.com/1277/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/orastory.wordpress.com/1277/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1277&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://orastory.wordpress.com/2011/10/27/what-job-runs-awr-snapshot/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">dombrooks</media:title>
		</media:content>
	</item>
		<item>
		<title>optimizer mode parameter, hint and missing statistics</title>
		<link>http://orastory.wordpress.com/2011/10/18/optimizer-mode-parameter-hint-and-missing-statistics/</link>
		<comments>http://orastory.wordpress.com/2011/10/18/optimizer-mode-parameter-hint-and-missing-statistics/#comments</comments>
		<pubDate>Tue, 18 Oct 2011 11:12:30 +0000</pubDate>
		<dc:creator>Dom Brooks</dc:creator>
				<category><![CDATA[optimizer]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[rbo]]></category>

		<guid isPermaLink="false">http://orastory.wordpress.com/?p=1267</guid>
		<description><![CDATA[On older versions at least, using an all_rows hint with the rule-based optimizer is not necessarily the same as setting optimizer_mode to all_rows. Edit: Note that I&#8217;m deliberately avoiding dynamic sampling and newer features like cardinality feedback, just showing how a little oddity in code paths for slightly different all_rows scenarios might lead to different [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1267&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>On older versions at least, using an all_rows hint with the rule-based optimizer is not necessarily the same as setting optimizer_mode to all_rows.</p>
<p>Edit:<br />
Note that I&#8217;m deliberately avoiding dynamic sampling and newer features like cardinality feedback, just showing how a little oddity in code paths for slightly different all_rows scenarios might lead to different estimates in older versions.</p>
<p>This resulted from a conversation with a colleague that started something like this:</p>
<p>Colleague: Is the default cardinality for a temporary table 2000?<br />
Me: No, it&#8217;s related to block size so you should see 8168.<br />
Colleague: No, I see 2000.</p>
<p>This was observed on 9.2.0.8 using a global temporary table but similar observations are possible with collections, external tables and even normal heap tables.</p>
<p>Yes, 9.2.0.8 is an old version, yes rule-based optimizer is now defunct, but this will remain relevant for the many systems that will continue to run old versions for years to come.</p>
<p>It is reasonably well-known that the default cardinality for collections and global temporary tables is related to block size &#8211; an overhead*. So, with an 8k block size, it is common to see a cardinality estimate of 8168, as shown below:</p>
<p><pre class="brush: plain;">
SQL&gt; alter session set optimizer_mode = all_rows;

Session altered.

SQL&gt; create global temporary table t1 
  2  (col1 number); 

Table created.

SQL&gt; explain plan for
  2  select * from t1;

Explained.

SQL&gt; select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |  8168 |   103K|    12   (9)|
|   1 |  TABLE ACCESS FULL   | T1          |  8168 |   103K|    12   (9)|
-------------------------------------------------------------------------

7 rows selected.

SQL&gt; 
</pre></p>
<p>There is no detailed explanation in the 10053 trace as to where the 8168 came from (it is documented &#8211; see further down the page):<br />
<pre class="brush: plain;">
BASE STATISTICAL INFORMATION
***********************
Table stats    Table: T1   Alias: T1
  TOTAL ::  (NOT ANALYZED)    CDN: 8168  NBLKS:  100  AVG_ROW_LEN:  100
</pre></p>
<p>However, if we step back in time and set optimizer_mode to rule but try to make amends using a statement level ALL_ROWS hint:</p>
<p><pre class="brush: plain;">
SQL&gt; alter session set optimizer_mode = rule;

Session altered.

SQL&gt; explain plan for
  2  select /*+ all_rows */ * from t1;

Explained.

SQL&gt; select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |  2000 | 26000 |    12   (9)|
|   1 |  TABLE ACCESS FULL   | T1          |  2000 | 26000 |    12   (9)|
-------------------------------------------------------------------------

7 rows selected.

SQL&gt; 
</pre></p>
<p>We fall into a different code path in the optimizer.</p>
<p>The following is found in a 10053 trace:<br />
<pre class="brush: plain;">
BASE STATISTICAL INFORMATION
***********************
Table stats    Table: T1   Alias: T1
  TOTAL ::  (NOT ANALYZED)    CDN: 2000  NBLKS:  100  AVG_ROW_LEN:  100
</pre></p>
<p>There&#8217;s no explanation regarding where the 2000 comes from but it is documented through versions 9.2 to 11gR2 as being <a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e16638/stats.htm#i41866">the default used for &#8220;Remote Cardinality&#8221;</a>.</p>
<p>So it seems that rule-based optimizer + all_rows hint drops into a different path in the optimizer which uses a default cardinality of 2000 rows.</p>
<p>The same behaviour is not true in the latest versions.</p>
<p>In 11gR2 you can still force the deprecated RBO but if you hint all_rows then you seem to go down a consistent path (i.e. consistent estimate of 8168 provided no dynamic sampling or cardinality feedback), as you might have expected above.</p>
<p>*Going back to the default value of <strong>8168</strong>, this is calculated from the formula of</p>
<blockquote><p>
num_of_blocks * (block_size &#8211; cache_layer) / avg_row_len
</p></blockquote>
<p>And for collections and global temporary tables without any additional statistics, num_of_blocks and avg_row_len default to 100 so we&#8217;re left with (block_size &#8211; cache_layer) with the cache_layer typically evaluating to 24.</p>
<p>In terms of the general application of this default formula, until recently I didn&#8217;t appreciate that in the absence of statistics AND dynamic sampling, that the actual number of blocks is used (if there is an actual number of blocks).</p>
<p>One of the reasons that it&#8217;s easy to have missed this formula is that in any recent version, dynamic sampling will kick in by default and so you have to explicitly prevent it. That&#8217;s my excuse anyway.</p>
<p><pre class="brush: plain;">
SQL&gt; drop table t1;

Table dropped.

SQL&gt; create table t1
  2  as
  3  select rownum col1
  4  from   dual
  5  connect by rownum &lt;= 100000;

Table created.

SQL&gt; alter session set optimizer_mode = all_rows;

Session altered.

SQL&gt; explain plan for
  2  select /*+ dynamic_sampling(t1 0) */ * from t1;

Explained.

SQL&gt; 
SQL&gt; select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             | 13314 |   169K|    18   (6)|
|   1 |  TABLE ACCESS FULL   | T1          | 13314 |   169K|    18   (6)|
-------------------------------------------------------------------------

7 rows selected.

SQL&gt; 
</pre></p>
<p>With the 10053 trace showing the blocks inputs:<br />
<pre class="brush: plain;">
BASE STATISTICAL INFORMATION
***********************
Table stats    Table: T1   Alias: T1
  TOTAL ::  (NOT ANALYZED)    CDN: 13314  NBLKS:  163  AVG_ROW_LEN:  100
</pre></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/orastory.wordpress.com/1267/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/orastory.wordpress.com/1267/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/orastory.wordpress.com/1267/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/orastory.wordpress.com/1267/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/orastory.wordpress.com/1267/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/orastory.wordpress.com/1267/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/orastory.wordpress.com/1267/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/orastory.wordpress.com/1267/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/orastory.wordpress.com/1267/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/orastory.wordpress.com/1267/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/orastory.wordpress.com/1267/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/orastory.wordpress.com/1267/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/orastory.wordpress.com/1267/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/orastory.wordpress.com/1267/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1267&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://orastory.wordpress.com/2011/10/18/optimizer-mode-parameter-hint-and-missing-statistics/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">dombrooks</media:title>
		</media:content>
	</item>
		<item>
		<title>Quick overview of loading plans into a baseline</title>
		<link>http://orastory.wordpress.com/2011/10/14/quick-overview-of-loading-plans-into-a-baseline/</link>
		<comments>http://orastory.wordpress.com/2011/10/14/quick-overview-of-loading-plans-into-a-baseline/#comments</comments>
		<pubDate>Fri, 14 Oct 2011 08:58:34 +0000</pubDate>
		<dc:creator>Dom Brooks</dc:creator>
				<category><![CDATA[11gR2]]></category>
		<category><![CDATA[oracle]]></category>
		<category><![CDATA[sql baselines]]></category>

		<guid isPermaLink="false">http://orastory.wordpress.com/?p=1257</guid>
		<description><![CDATA[Quick overview of DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE and DBMS_SPM.LOAD_PLANS_FROM_SQLSET: Loads plans into baselines from cursor cache, for example get all plans with a certain MODULE or a certain parsing schema. Or just everything. Load it all up (NOT). Loads a specific sql_id, and a specific plan if specified by hash, into a baseline (must be an existing sql [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1257&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Quick overview of DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE and DBMS_SPM.LOAD_PLANS_FROM_SQLSET:</p>
<p><pre class="brush: plain;">
load_plans_from_cursor_cache
(attribute_name   IN VARCHAR2,
 attribute_value  IN VARCHAR2,
 fixed            IN VARCHAR2 := 'NO',
 enabled          IN VARCHAR2 := 'YES')
</pre><br />
Loads plans into baselines from cursor cache, for example get all plans with a certain MODULE or a certain parsing schema. Or just everything. Load it all up (NOT).</p>
<p><pre class="brush: plain;"> 
load_plans_from_cursor_cache
(sql_id           IN VARCHAR2,
 plan_hash_value  IN NUMBER := NULL,
 fixed            IN VARCHAR2 := 'NO',
 enabled          IN VARCHAR2 := 'YES')
</pre><br />
Loads a specific sql_id, and a specific plan if specified by hash, into a baseline (must be an existing sql id and plan_hash).</p>
<p><pre class="brush: plain;">                                       
load_plans_from_cursor_cache
(sql_id           IN VARCHAR2,
 plan_hash_value  IN NUMBER := NULL,
 sql_text         IN CLOB,
 fixed            IN VARCHAR2 := 'NO',
 enabled          IN VARCHAR2 := 'YES')
</pre></p>
<p>Allows you to use a baseline to apply a specific plan in the cache (must be an existing sql id and plan_hash)<br />
to another sql statement (sql_text). Remember baselines are by signature so all we need is the sql text which will be passed through a filter to make it space and case desensitised. But using sql_text is a bit unwieldy, particularly for large statements so see method below.</p>
<p><pre class="brush: plain;">                                       
load_plans_from_cursor_cache
(sql_id           IN VARCHAR2,
 plan_hash_value  IN NUMBER := NULL,
 sql_handle       IN VARCHAR2,
 fixed            IN VARCHAR2 := 'NO',
 enabled          IN VARCHAR2 := 'YES')
</pre></p>
<p>Similar to above, allows to you take an existing plan and apply it to an existing sql_handle in dba_sql_plan_baselines.</p>
<p>Using sql_text in previous method is not that convenient if you&#8217;re got big statements, etc. So you could load up the existing plans into a baseline, disable the existing baselined plans, then load up the desired plan from source statement to the target statement.</p>
<p>What is sql_handle?<br />
Thanks to Hemant for pointing me in the direction of <a href="http://oracleprof.blogspot.com/2011/07/how-to-find-sqlid-and-planhashvalue-in.html">Marcin Przepiorowski &#8211; &#8220;SQL_HANDLE contain hexadecimal representation of EXACT_MATCHING_SIGNATURE from V$SQL&#8221;</a>. I&#8217;d read that article before but that that particular info didn&#8217;t sink in.</p>
<p><pre class="brush: plain;">                                       
load_plans_from_sqlset
(sqlset_name        IN VARCHAR2,
 sqlset_owner       IN VARCHAR2 := NULL,
 basic_filter       IN VARCHAR2 := NULL,
 fixed              IN VARCHAR2 := 'NO',
 enabled            IN VARCHAR2 := 'YES',
 commit_rows        IN NUMBER := 1000)
</pre></p>
<p>Loads plans into baselines from an existing sql tuning set.<br />
Here&#8217;s an example on loading up the top 10 statments by elapsed time between two awr snaps into<br />
a sqlset then a baseline:</p>
<p><pre class="brush: plain;">
DECLARE
 baseline_cursor DBMS_SQLTUNE.SQLSET_CURSOR;
BEGIN
 OPEN baseline_cursor FOR
      SELECT VALUE(p)
      FROM   TABLE(dbms_sqltune.select_workload_repository
             (begin_snap        =&gt; &lt;begin_snap&gt;,
              end_snap          =&gt; &lt;end_snap&gt;,
              basic_filter      =&gt; NULL,
              ranking_measure1  =&gt; 'elapsed_time',
              result_limit      =&gt; 10,
              attribute_list    =&gt; 'ALL'
              )) p; 
 DBMS_SQLTUNE.LOAD_SQLSET
 (sqlset_name     =&gt; 'my_test__tuning_set',
  populate_cursor =&gt; baseline_cursor);
 DBMS_SPM.load_plans_from_sqlset
 (sqlset_name        =&gt; 'my_test__tuning_set');
END;
/
</pre></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/orastory.wordpress.com/1257/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/orastory.wordpress.com/1257/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/orastory.wordpress.com/1257/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/orastory.wordpress.com/1257/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/orastory.wordpress.com/1257/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/orastory.wordpress.com/1257/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/orastory.wordpress.com/1257/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/orastory.wordpress.com/1257/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/orastory.wordpress.com/1257/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/orastory.wordpress.com/1257/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/orastory.wordpress.com/1257/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/orastory.wordpress.com/1257/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/orastory.wordpress.com/1257/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/orastory.wordpress.com/1257/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=orastory.wordpress.com&amp;blog=672199&amp;post=1257&amp;subd=orastory&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://orastory.wordpress.com/2011/10/14/quick-overview-of-loading-plans-into-a-baseline/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">dombrooks</media:title>
		</media:content>
	</item>
	</channel>
</rss>
