<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	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: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>Comments on: Index Join &#8211; 2</title>
	<atom:link href="http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/feed/" rel="self" type="application/rss+xml" />
	<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/</link>
	<description>Just another Oracle weblog</description>
	<lastBuildDate>Fri, 24 May 2013 15:10:42 +0000</lastBuildDate>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
	<item>
		<title>By: Pavol Babel</title>
		<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/#comment-37941</link>
		<dc:creator><![CDATA[Pavol Babel]]></dc:creator>
		<pubDate>Tue, 30 Nov 2010 00:38:07 +0000</pubDate>
		<guid isPermaLink="false">http://jonathanlewis.wordpress.com/?p=4847#comment-37941</guid>
		<description><![CDATA[Jonathan,

I&#039;ve just found mentioned SR. It was created by me as SR 2-4140118 (legacy SR number 6614847.993, Opened Dec 4, 2007 9:14 PM, Last Updated Feb 13, 2008 1:40 AM). Unfortunately, I&#039;m not able to see the content (oracle has already deleted it ), but following bug was created because of my SR: BUG 6692628  IGNORED LEADING HINTS WITH VIEWS . The bug is available to public.
So I&#039;m pleased to now that I have now my own example in 11gR2 documentation, thank you Jonathan!! :-) :-)  (btw, it is not in 11gR1 documentation).  The bug was closed as &quot;not a bug&quot;, but someone has obviously added this &quot;issue&quot; to 11gR2 documentation without any mention in BUG document.]]></description>
		<content:encoded><![CDATA[<p>Jonathan,</p>
<p>I&#8217;ve just found mentioned SR. It was created by me as SR 2-4140118 (legacy SR number 6614847.993, Opened Dec 4, 2007 9:14 PM, Last Updated Feb 13, 2008 1:40 AM). Unfortunately, I&#8217;m not able to see the content (oracle has already deleted it ), but following bug was created because of my SR: BUG 6692628  IGNORED LEADING HINTS WITH VIEWS . The bug is available to public.<br />
So I&#8217;m pleased to now that I have now my own example in 11gR2 documentation, thank you Jonathan!! :-) :-)  (btw, it is not in 11gR1 documentation).  The bug was closed as &#8220;not a bug&#8221;, but someone has obviously added this &#8220;issue&#8221; to 11gR2 documentation without any mention in BUG document.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Pavol Babel</title>
		<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/#comment-37940</link>
		<dc:creator><![CDATA[Pavol Babel]]></dc:creator>
		<pubDate>Mon, 29 Nov 2010 23:57:10 +0000</pubDate>
		<guid isPermaLink="false">http://jonathanlewis.wordpress.com/?p=4847#comment-37940</guid>
		<description><![CDATA[Jonathan,

the SR I&#039;ve mentioned had been created few years ago. I&#039;m not sure if it was created before the 11gr1 or not. I&#039;ll try to find it in my archives.
But things have obviously changed in 11gR2, thank you very much for this link Most of our databases are still running 10.2.0.4 or 10.2.0.5, I have never cheked this part of documentation in 11g.
The most crazy thing about example 19-4 is, that it is exactly the select taken from  SR, which I was talking about. Tables A, B, C, where view V is defined as join of tables B and C. And join of table A with view V. And alias a for table a, alias v for view v. No one in the world would be so crazy to use such aliases, except of me. Yes it must be taken from my SR, I&#039;m not joking!!! :-) :-)]]></description>
		<content:encoded><![CDATA[<p>Jonathan,</p>
<p>the SR I&#8217;ve mentioned had been created few years ago. I&#8217;m not sure if it was created before the 11gr1 or not. I&#8217;ll try to find it in my archives.<br />
But things have obviously changed in 11gR2, thank you very much for this link Most of our databases are still running 10.2.0.4 or 10.2.0.5, I have never cheked this part of documentation in 11g.<br />
The most crazy thing about example 19-4 is, that it is exactly the select taken from  SR, which I was talking about. Tables A, B, C, where view V is defined as join of tables B and C. And join of table A with view V. And alias a for table a, alias v for view v. No one in the world would be so crazy to use such aliases, except of me. Yes it must be taken from my SR, I&#8217;m not joking!!! :-) :-)</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonathan Lewis</title>
		<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/#comment-37937</link>
		<dc:creator><![CDATA[Jonathan Lewis]]></dc:creator>
		<pubDate>Mon, 29 Nov 2010 23:02:54 +0000</pubDate>
		<guid isPermaLink="false">http://jonathanlewis.wordpress.com/?p=4847#comment-37937</guid>
		<description><![CDATA[Pavel,

&lt;ul&gt;&lt;i&gt;
&quot;(t@qb_name reference is undocumented, too and not approved by Oracle Support in SR).&quot;
&lt;/i&gt;&lt;/ul&gt;

In the 11.2 Performance Tuning Guide Section 19.2.2, there is an example of using the execution plan to discover the query block name of a query block that you have not named (admittedly the example happens to be a boring &lt;i&gt;&quot;sel$4&quot;&lt;/i&gt;)

Then, in Section 19.2.3, example 19-4, we see this:
&lt;ul&gt;&lt;i&gt;
To avoid this issue, Oracle recommends that you specify a query block in the hint using the @SEL notation:

SELECT /*+ LEADING(A@SEL$1 B@SEL$2 C@SEL$2) */
FROM a a, v v
WHERE a.id = v.id;
&lt;/i&gt;&lt;/ul&gt;

I find it faintly amazing that Oracle support should tell you that &quot;alias@qb_name&quot; is not approved.]]></description>
		<content:encoded><![CDATA[<p>Pavel,</p>
<ul><i><br />
&#8220;(t@qb_name reference is undocumented, too and not approved by Oracle Support in SR).&#8221;<br />
</i></ul>
<p>In the 11.2 Performance Tuning Guide Section 19.2.2, there is an example of using the execution plan to discover the query block name of a query block that you have not named (admittedly the example happens to be a boring <i>&#8220;sel$4&#8243;</i>)</p>
<p>Then, in Section 19.2.3, example 19-4, we see this:</p>
<ul><i><br />
To avoid this issue, Oracle recommends that you specify a query block in the hint using the @SEL notation:</p>
<p>SELECT /*+ LEADING(A@SEL$1 B@SEL$2 C@SEL$2) */<br />
FROM a a, v v<br />
WHERE a.id = v.id;<br />
</i></ul>
<p>I find it faintly amazing that Oracle support should tell you that &#8220;alias@qb_name&#8221; is not approved.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Pavol Babel</title>
		<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/#comment-37930</link>
		<dc:creator><![CDATA[Pavol Babel]]></dc:creator>
		<pubDate>Mon, 29 Nov 2010 00:02:27 +0000</pubDate>
		<guid isPermaLink="false">http://jonathanlewis.wordpress.com/?p=4847#comment-37930</guid>
		<description><![CDATA[One more remark. You have made me running 10053 on query from my example just by using abbreviation &quot;JPPD&quot;. Last time I ran 10053 on query with blocked join predicate pushing (by no_merge hint), it was few years ago, in 9iR2 or 10gR1. As you know, there were no explanations of abbrevations Like JPPD, OJPPD, SPJ (etc.) even in 10gR. It was sometimes really difficult to address any Query Transformation related issue. The LEGEND in 10053 in 10gR2 and in 11g is great, I wish I had it few years ago! :)]]></description>
		<content:encoded><![CDATA[<p>One more remark. You have made me running 10053 on query from my example just by using abbreviation &#8220;JPPD&#8221;. Last time I ran 10053 on query with blocked join predicate pushing (by no_merge hint), it was few years ago, in 9iR2 or 10gR1. As you know, there were no explanations of abbrevations Like JPPD, OJPPD, SPJ (etc.) even in 10gR. It was sometimes really difficult to address any Query Transformation related issue. The LEGEND in 10053 in 10gR2 and in 11g is great, I wish I had it few years ago! :)</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Pavol Babel</title>
		<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/#comment-37929</link>
		<dc:creator><![CDATA[Pavol Babel]]></dc:creator>
		<pubDate>Sun, 28 Nov 2010 23:49:10 +0000</pubDate>
		<guid isPermaLink="false">http://jonathanlewis.wordpress.com/?p=4847#comment-37929</guid>
		<description><![CDATA[Now I understand, you had some problems with no_merge in the past, that’s what I initially expected. Maybe I just didn’t understand your remark “I think you’re just trying to make things difficult for me”  Join push predicate is probably the biggest issue correlated to no_merge hint.

You’re absolutely right with transitive closure my complaint is really wrong, sorry for that.

Back to my example. The optimizer is considering my preferred execution plan, of course. I would like to point out – I just wanted to create some simple example to show join predicate pushing problem. My initial intention was just to say “imagine you need use index on t2, but optimizer chooses index on t3″. But then I invented simple trick with data in table t2, and oracle really did not choose the best index, making by example better.
Real queries in our productional system are much more complex, imagine joining 5 views, each of them based on join of two or three tables. Some of them are still causing me headaches. I would desperately like to have control over join order of tables in some cases, but it seems to be not doable without some little help of undocumented hints (t@qb_name reference is undocumented, too and not approved by Oracle Support in SR).

Hacking, or let me say “improving”, statistics with dbms_stats.set_%_stats procedures is really great technique helping in many situations. But as you suggests, it is not usable every time.

Your next suggestion is to use CARDINALITY hint. We have tried to use OPT_ESTIMATE hint (or the older one CARDINALITY) hints several times in the past, but we didn’t get the approval from Oracle Support. To be honest, I do not know why and it is quite irritating us. There are many situations where you know optimizer’s arithmetic is wrong, so why are you not allowed to correct it in simple way? Most of our customers are not going to pay extra licenses for SQL TUNING ADVISOR (part of TUNING PACK, which requires also DIAGNOSTICS PACK) just because of lack opt_estimate/cardinality hint support.]]></description>
		<content:encoded><![CDATA[<p>Now I understand, you had some problems with no_merge in the past, that’s what I initially expected. Maybe I just didn’t understand your remark “I think you’re just trying to make things difficult for me”  Join push predicate is probably the biggest issue correlated to no_merge hint.</p>
<p>You’re absolutely right with transitive closure my complaint is really wrong, sorry for that.</p>
<p>Back to my example. The optimizer is considering my preferred execution plan, of course. I would like to point out – I just wanted to create some simple example to show join predicate pushing problem. My initial intention was just to say “imagine you need use index on t2, but optimizer chooses index on t3″. But then I invented simple trick with data in table t2, and oracle really did not choose the best index, making by example better.<br />
Real queries in our productional system are much more complex, imagine joining 5 views, each of them based on join of two or three tables. Some of them are still causing me headaches. I would desperately like to have control over join order of tables in some cases, but it seems to be not doable without some little help of undocumented hints (t@qb_name reference is undocumented, too and not approved by Oracle Support in SR).</p>
<p>Hacking, or let me say “improving”, statistics with dbms_stats.set_%_stats procedures is really great technique helping in many situations. But as you suggests, it is not usable every time.</p>
<p>Your next suggestion is to use CARDINALITY hint. We have tried to use OPT_ESTIMATE hint (or the older one CARDINALITY) hints several times in the past, but we didn’t get the approval from Oracle Support. To be honest, I do not know why and it is quite irritating us. There are many situations where you know optimizer’s arithmetic is wrong, so why are you not allowed to correct it in simple way? Most of our customers are not going to pay extra licenses for SQL TUNING ADVISOR (part of TUNING PACK, which requires also DIAGNOSTICS PACK) just because of lack opt_estimate/cardinality hint support.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonathan Lewis</title>
		<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/#comment-37923</link>
		<dc:creator><![CDATA[Jonathan Lewis]]></dc:creator>
		<pubDate>Sun, 28 Nov 2010 14:49:52 +0000</pubDate>
		<guid isPermaLink="false">http://jonathanlewis.wordpress.com/?p=4847#comment-37923</guid>
		<description><![CDATA[Pavel,

I&#039;ve run your example on my laptop and checked a few details.
The plan you want to see is one that the optimizer considers - but it doesn&#039;t like the results of arithmetic. This means we can get the optimizer to do what you want by changing some numbers. The key thing is that Oracle thinks using the index on t3 is cheaper than the index on t2 and that the join result from t3 is much smaller than the join result on t2. So we change its mind.

If this is a general problem with many queries, we could get the effect we want by adjusting the clustering_factor (or blevel) on the t2 and t3 indexes. But if we just want to fix this specific example we&#039;re a bit lucky because we can put in cardinality hints that Oracle can apply without having to use derived query block names.

[sourcecode gutter=&quot;false&quot;]
SELECT 
	/*+ 
		cardinality(t1@sel$1  50)
		cardinality(t1@sel$1  t2@sel$2 50)
		cardinality(t1@sel$1  t3@sel$2 200)
	*/
	* 
FROM t1, v
WHERE t1.join1 = v.join2
  AND t1.join1 = v.join3
  AND t1.filter1 BETWEEN &#039;0000000050&#039; AND &#039;0000000100&#039;
;

[/sourcecode]

We tell the optimizer how many rows we think will come from t1, then how many will come from each of the next joins - claiming that the join to t3 will produce a lot more data than the join to t2. With these figures, this is the plan I got:

[sourcecode gutter=&quot;false&quot;]
------------------------------------------------------------------------
&#124; Id  &#124; Operation                      &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost  &#124;
------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT               &#124;       &#124;     1 &#124;    45 &#124;   703 &#124;
&#124;   1 &#124;  NESTED LOOPS                  &#124;       &#124;       &#124;       &#124;       &#124;
&#124;   2 &#124;   NESTED LOOPS                 &#124;       &#124;     1 &#124;    45 &#124;   703 &#124;
&#124;   3 &#124;    NESTED LOOPS                &#124;       &#124;    50 &#124;  1500 &#124;   603 &#124;
&#124;   4 &#124;     TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;    50 &#124;   750 &#124;     3 &#124;
&#124;*  5 &#124;      INDEX RANGE SCAN          &#124; T1_I1 &#124;    52 &#124;       &#124;     2 &#124;
&#124;   6 &#124;     TABLE ACCESS BY INDEX ROWID&#124; T2    &#124;    10 &#124;   150 &#124;    12 &#124;
&#124;*  7 &#124;      INDEX RANGE SCAN          &#124; T2_I1 &#124;    10 &#124;       &#124;     2 &#124;
&#124;*  8 &#124;    INDEX RANGE SCAN            &#124; T3_I1 &#124;     1 &#124;       &#124;     1 &#124;
&#124;   9 &#124;   TABLE ACCESS BY INDEX ROWID  &#124; T3    &#124;     1 &#124;    15 &#124;     2 &#124;
------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access(&quot;T1&quot;.&quot;FILTER1&quot;&gt;=&#039;0000000050&#039; AND
              &quot;T1&quot;.&quot;FILTER1&quot;&lt;=&#039;0000000100&#039;)
   7 - access(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T2&quot;.&quot;JOIN2&quot;)
   8 - access(&quot;T2&quot;.&quot;JOIN2&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)
       filter(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)

[/sourcecode]]]></description>
		<content:encoded><![CDATA[<p>Pavel,</p>
<p>I&#8217;ve run your example on my laptop and checked a few details.<br />
The plan you want to see is one that the optimizer considers &#8211; but it doesn&#8217;t like the results of arithmetic. This means we can get the optimizer to do what you want by changing some numbers. The key thing is that Oracle thinks using the index on t3 is cheaper than the index on t2 and that the join result from t3 is much smaller than the join result on t2. So we change its mind.</p>
<p>If this is a general problem with many queries, we could get the effect we want by adjusting the clustering_factor (or blevel) on the t2 and t3 indexes. But if we just want to fix this specific example we&#8217;re a bit lucky because we can put in cardinality hints that Oracle can apply without having to use derived query block names.</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
SELECT 
	/*+ 
		cardinality(t1@sel$1  50)
		cardinality(t1@sel$1  t2@sel$2 50)
		cardinality(t1@sel$1  t3@sel$2 200)
	*/
	* 
FROM t1, v
WHERE t1.join1 = v.join2
  AND t1.join1 = v.join3
  AND t1.filter1 BETWEEN '0000000050' AND '0000000100'
;

</pre>
<p>We tell the optimizer how many rows we think will come from t1, then how many will come from each of the next joins &#8211; claiming that the join to t3 will produce a lot more data than the join to t2. With these figures, this is the plan I got:</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
------------------------------------------------------------------------
| Id  | Operation                      | Name  | Rows  | Bytes | Cost  |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |       |     1 |    45 |   703 |
|   1 |  NESTED LOOPS                  |       |       |       |       |
|   2 |   NESTED LOOPS                 |       |     1 |    45 |   703 |
|   3 |    NESTED LOOPS                |       |    50 |  1500 |   603 |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1    |    50 |   750 |     3 |
|*  5 |      INDEX RANGE SCAN          | T1_I1 |    52 |       |     2 |
|   6 |     TABLE ACCESS BY INDEX ROWID| T2    |    10 |   150 |    12 |
|*  7 |      INDEX RANGE SCAN          | T2_I1 |    10 |       |     2 |
|*  8 |    INDEX RANGE SCAN            | T3_I1 |     1 |       |     1 |
|   9 |   TABLE ACCESS BY INDEX ROWID  | T3    |     1 |    15 |     2 |
------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access(&quot;T1&quot;.&quot;FILTER1&quot;&gt;='0000000050' AND
              &quot;T1&quot;.&quot;FILTER1&quot;&lt;='0000000100')
   7 - access(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T2&quot;.&quot;JOIN2&quot;)
   8 - access(&quot;T2&quot;.&quot;JOIN2&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)
       filter(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)

</pre>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonathan Lewis</title>
		<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/#comment-37922</link>
		<dc:creator><![CDATA[Jonathan Lewis]]></dc:creator>
		<pubDate>Sun, 28 Nov 2010 12:21:39 +0000</pubDate>
		<guid isPermaLink="false">http://jonathanlewis.wordpress.com/?p=4847#comment-37922</guid>
		<description><![CDATA[Pavel,

I didn&#039;t say I hadn&#039;t had problems with the &lt;em&gt;no_merge &lt;/em&gt;hint - just that I was interested to see what problems you have had ... and your&#039;s is the same as the one that irritates me most: using the &lt;em&gt;no_merge&lt;/em&gt; hint blocks join predicate pushdown.  Things have improved in 11g, but even then the restriction has only been relaxed to cover &quot;complex view merging (CVM)&quot; - if you use the no_merge hint to block &quot;simple view merging (SVM)&quot; then join predicate push down (JPPD) is still blocked.

I&#039;m not sure your complaint about transitive closure is valid (or at least, not in the context of the optimizer). &lt;a href=&quot;http://jonathanlewis.wordpress.com/2007/01/01/transitive-closure/&quot; rel=&quot;nofollow&quot;&gt;&lt;em&gt;&lt;strong&gt;Transitive closure&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt; only applies in Oracle for:  &lt;em&gt;&quot;if colA = colB and colB = constant then colA = constant&quot;&lt;/em&gt; - and I think you&#039;re hoping that your example will cover &lt;em&gt;&quot;colA = colB and colB = colC&quot;&lt;/em&gt;, where it doesn&#039;t apply.]]></description>
		<content:encoded><![CDATA[<p>Pavel,</p>
<p>I didn&#8217;t say I hadn&#8217;t had problems with the <em>no_merge </em>hint &#8211; just that I was interested to see what problems you have had &#8230; and your&#8217;s is the same as the one that irritates me most: using the <em>no_merge</em> hint blocks join predicate pushdown.  Things have improved in 11g, but even then the restriction has only been relaxed to cover &#8220;complex view merging (CVM)&#8221; &#8211; if you use the no_merge hint to block &#8220;simple view merging (SVM)&#8221; then join predicate push down (JPPD) is still blocked.</p>
<p>I&#8217;m not sure your complaint about transitive closure is valid (or at least, not in the context of the optimizer). <a href="http://jonathanlewis.wordpress.com/2007/01/01/transitive-closure/" rel="nofollow"><em><strong>Transitive closure</strong></em></a> only applies in Oracle for:  <em>&#8220;if colA = colB and colB = constant then colA = constant&#8221;</em> &#8211; and I think you&#8217;re hoping that your example will cover <em>&#8220;colA = colB and colB = colC&#8221;</em>, where it doesn&#8217;t apply.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: pbabel</title>
		<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/#comment-37916</link>
		<dc:creator><![CDATA[pbabel]]></dc:creator>
		<pubDate>Sun, 28 Nov 2010 02:23:45 +0000</pubDate>
		<guid isPermaLink="false">http://jonathanlewis.wordpress.com/?p=4847#comment-37916</guid>
		<description><![CDATA[Jonathan,

great job by using undocumented hint outline_leaf. I&#039;m not going to use it in my production database, of course. But to be honest, it still looks much better than my hint using &quot;@SEL$9B45E752&quot;.

Actually, I&#039;m quite surprised you&#039;ve never faced any problems with NO_MERGE hint. I&#039;ll try to show you another simplified example. Since my english is far from perfect,  I hope you will get the point.

[sourcecode language=&quot;css&quot;]

DROP TABLE t1 PURGE;
DROP TABLE t2 PURGE;
DROP TABLE t3 PURGE;
CREATE TABLE t1 (join1 NUMBER, filter1 VARCHAR2(30));
CREATE TABLE t2 (join2 NUMBER, filter2 VARCHAR2(30));
CREATE TABLE t3 (join3 NUMBER, filter3 VARCHAR2(30));


INSERT /*+ APPEND */ INTO t1
WITH tmp1 AS (SELECT /*+ MATERIALIZE */ 1 FROM all_objects WHERE rownum &lt;= 1000)
SELECT rownum, lpad(rownum, 10, &#039;0&#039;) FROM tmp1, tmp1 WHERE rownum &lt;= 100000
/
COMMIT;

BEGIN
  FOR i IN 1..10 LOOP
    INSERT /*+ APPEND */ INTO t2
      --skipping some data from t1
      SELECT * FROM t1  WHERE NOT(t1.filter1 BETWEEN &#039;0000000051&#039; AND &#039;0000000100&#039;);
    COMMIT;
  END LOOP;

  FOR i IN 1..2 LOOP
    INSERT /*+ APPEND */ INTO t3
      SELECT * FROM t1;
    COMMIT;
  END LOOP;  
END;
/

CREATE INDEX t1_i1 ON t1(filter1);
CREATE INDEX t2_i1 ON t2(join2);
CREATE INDEX t3_i1 ON t3(join3);

exec dbms_stats.gather_table_stats(user, &#039;T1&#039;, method_opt =&gt; &#039;FOR ALL COLUMNS SIZE 1&#039;);
exec dbms_stats.gather_table_stats(user, &#039;T2&#039;, method_opt =&gt; &#039;FOR ALL COLUMNS SIZE 1&#039;);
exec dbms_stats.gather_table_stats(user, &#039;T3&#039;, method_opt =&gt; &#039;FOR ALL COLUMNS SIZE 1&#039;);

-- changing view definition is not allowed, again (black box application)
CREATE OR REPLACE VIEW v AS
SELECT t2.join2, t2.filter2, t3.join3, t3.filter3
  FROM t2, t3
 WHERE t2.join2 = t3.join3
/

--parameters to reproduce execution plan
ALTER SESSION SET &quot;_optimizer_cost_model&quot; = io;
ALTER SESSION SET db_file_multiblock_read_count = 8;
ALTER SESSION SET work_area_size_policy=MANUAL;
ALTER SESSION SET hash_area_size=131072;

SELECT * FROM t1, v
WHERE t1.join1 = v.join2
  AND t1.join1 = v.join3
  AND t1.filter1 BETWEEN &#039;0000000050&#039; AND &#039;0000000100&#039;
/

[/sourcecode]

The first oddity is that optimizer on my 10.2.0.5 database is not able to use transitive rule int this simple case, that&#039;s why both predicates have been used (t1.join1 = v.join2 AND  t1.join1 = v.join3) . The query plan for this query (hope to be same on most 10g databases)


[sourcecode language=&quot;css&quot;]

------------------------------------------------------------------------
&#124; Id  &#124; Operation                      &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost  &#124;
------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT               &#124;       &#124;     1 &#124;    45 &#124;   210 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID   &#124; T2    &#124;     1 &#124;    15 &#124;     1 &#124;
&#124;   2 &#124;   NESTED LOOPS                 &#124;       &#124;     1 &#124;    45 &#124;   210 &#124;
&#124;   3 &#124;    NESTED LOOPS                &#124;       &#124;   104 &#124;  3120 &#124;   107 &#124;
&#124;   4 &#124;     TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;    52 &#124;   780 &#124;     3 &#124;
&#124;*  5 &#124;      INDEX RANGE SCAN          &#124; T1_I1 &#124;    52 &#124;       &#124;     2 &#124;
&#124;   6 &#124;     TABLE ACCESS BY INDEX ROWID&#124; T3    &#124;     2 &#124;    30 &#124;     2 &#124;
&#124;*  7 &#124;      INDEX RANGE SCAN          &#124; T3_I1 &#124;     2 &#124;       &#124;       &#124;
&#124;*  8 &#124;    INDEX RANGE SCAN            &#124; T2_I1 &#124;     1 &#124;       &#124;       &#124;
------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access(&quot;T1&quot;.&quot;FILTER1&quot;&gt;=&#039;0000000050&#039; AND
              &quot;T1&quot;.&quot;FILTER1&quot;&lt;=&#039;0000000100&#039;)
   7 - access(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)
   8 - access(&quot;T2&quot;.&quot;JOIN2&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)
       filter(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T2&quot;.&quot;JOIN2&quot;)

[/sourcecode]

The optimizer does not have many opportunities to discover the little &quot;hole&quot; in table2, even with histograms on table t2. But the correct join order should be t1 -&gt; t2 -&gt; t3 .
Since only tables at the &quot;same level&quot; in FROM clause should be used in LEADING HINT, you are not able to change the join order between tables t2 and t3.

[sourcecode language=&quot;css&quot;]
SELECT /*+ LEADING(t1 v.t2 v.t3)
           USE_NL(v.t2) INDEX(v.t2)
           USE_NL(v.t3) INDEX(v.t3) */
--leading HINT ignored, not correct use
   *
 FROM t1, v
WHERE t1.join1 = v.join2
  AND t1.join1 = v.join3
  AND t1.filter1 BETWEEN &#039;0000000050&#039; AND &#039;0000000100&#039;
/
[/sourcecode]

Unfortunately, the NO_MERGE hint on view would make it even worse, it would prevent from pushing the join predicate into VIEW. When NO_MERGE used, I&#039;m unable to use any further documented hint to make things work. Here is my best try, but no success. 

Note the INDEX FULL SCAN on line 8 making execution plan suboptimal. 

[sourcecode language=&quot;css&quot;]
SELECT /*+ NO_MERGE(v) PUSH_PRED(v)
           LEADING(t1 v) USE_NL(v)
           LEADING(v.t2 v.t3)
           INDEX(v.t2) 
           USE_NL(v.t3) INDEX(v.t2)
       */
   *
 FROM t1, v
WHERE t1.join1 = v.join2
  AND t1.join1 = v.join3
  AND t1.filter1 BETWEEN &#039;0000000050&#039; AND &#039;0000000100&#039;
/ 


-------------------------------------------------------------------------
&#124; Id  &#124; Operation                       &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost  &#124;
-------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT                &#124;       &#124;  1034 &#124; 77550 &#124;   155M&#124;
&#124;   1 &#124;  NESTED LOOPS                   &#124;       &#124;  1034 &#124; 77550 &#124;   155M&#124;
&#124;   2 &#124;   TABLE ACCESS BY INDEX ROWID   &#124; T1    &#124;    52 &#124;   780 &#124;     3 &#124;
&#124;*  3 &#124;    INDEX RANGE SCAN             &#124; T1_I1 &#124;    52 &#124;       &#124;     2 &#124;
&#124;*  4 &#124;   VIEW                          &#124; V     &#124;    20 &#124;  1200 &#124;  2990K&#124;
&#124;   5 &#124;    TABLE ACCESS BY INDEX ROWID  &#124; T3    &#124;     2 &#124;    30 &#124;     2 &#124;
&#124;   6 &#124;     NESTED LOOPS                &#124;       &#124;  1988K&#124;    56M&#124;  2990K&#124;
&#124;   7 &#124;      TABLE ACCESS BY INDEX ROWID&#124; T2    &#124;   994K&#124;    14M&#124;  1001K&#124;
&#124;   8 &#124;       INDEX FULL SCAN           &#124; T2_I1 &#124;   999K&#124;       &#124;  2222 &#124;
&#124;*  9 &#124;      INDEX RANGE SCAN           &#124; T3_I1 &#124;     2 &#124;       &#124;       &#124;
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access(&quot;T1&quot;.&quot;FILTER1&quot;&gt;=&#039;0000000050&#039; AND
              &quot;T1&quot;.&quot;FILTER1&quot;&lt;=&#039;0000000100&#039;)
   4 - filter(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;V&quot;.&quot;JOIN2&quot; AND &quot;T1&quot;.&quot;JOIN1&quot;=&quot;V&quot;.&quot;JOIN3&quot;)
   9 - access(&quot;T2&quot;.&quot;JOIN2&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)


[/sourcecode]

The only way I&#039;m able to force optimizer choose the best optimal execution plan is using system generated query block names, again.

[sourcecode]
SELECT
   /*+ LEADING(@SEL$F5BB74E1 t1@SEL$1 t2@SEL$2 t3@SEL$2)
         INDEX(t1)
         USE_NL(v.t2) INDEX(v.t2)
         USE_NL(v.t3) INDEX(v.t3)
   */
     *
 FROM t1, v
WHERE t1.join1 = v.join2
  AND t1.join1 = v.join3
  AND t1.filter1 BETWEEN &#039;0000000050&#039; AND &#039;0000000100&#039;
/

------------------------------------------------------------------------
&#124; Id  &#124; Operation                      &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost  &#124;
------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT               &#124;       &#124;     1 &#124;    45 &#124;  1092 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID   &#124; T3    &#124;     1 &#124;    15 &#124;     1 &#124;
&#124;   2 &#124;   NESTED LOOPS                 &#124;       &#124;     1 &#124;    45 &#124;  1092 &#124;
&#124;   3 &#124;    NESTED LOOPS                &#124;       &#124;   521 &#124; 15630 &#124;   571 &#124;
&#124;   4 &#124;     TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;    52 &#124;   780 &#124;     3 &#124;
&#124;*  5 &#124;      INDEX RANGE SCAN          &#124; T1_I1 &#124;    52 &#124;       &#124;     2 &#124;
&#124;   6 &#124;     TABLE ACCESS BY INDEX ROWID&#124; T2    &#124;    10 &#124;   150 &#124;    11 &#124;
&#124;*  7 &#124;      INDEX RANGE SCAN          &#124; T2_I1 &#124;    10 &#124;       &#124;       &#124;
&#124;*  8 &#124;    INDEX RANGE SCAN            &#124; T3_I1 &#124;     1 &#124;       &#124;       &#124;
------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access(&quot;T1&quot;.&quot;FILTER1&quot;&gt;=&#039;0000000050&#039; AND
              &quot;T1&quot;.&quot;FILTER1&quot;&lt;=&#039;0000000100&#039;)
   7 - access(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T2&quot;.&quot;JOIN2&quot;)
   8 - access(&quot;T2&quot;.&quot;JOIN2&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)
       filter(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)


[/sourcecode]]]></description>
		<content:encoded><![CDATA[<p>Jonathan,</p>
<p>great job by using undocumented hint outline_leaf. I&#8217;m not going to use it in my production database, of course. But to be honest, it still looks much better than my hint using &#8220;@SEL$9B45E752&#8243;.</p>
<p>Actually, I&#8217;m quite surprised you&#8217;ve never faced any problems with NO_MERGE hint. I&#8217;ll try to show you another simplified example. Since my english is far from perfect,  I hope you will get the point.</p>
<pre class="brush: css; title: ; notranslate">

DROP TABLE t1 PURGE;
DROP TABLE t2 PURGE;
DROP TABLE t3 PURGE;
CREATE TABLE t1 (join1 NUMBER, filter1 VARCHAR2(30));
CREATE TABLE t2 (join2 NUMBER, filter2 VARCHAR2(30));
CREATE TABLE t3 (join3 NUMBER, filter3 VARCHAR2(30));


INSERT /*+ APPEND */ INTO t1
WITH tmp1 AS (SELECT /*+ MATERIALIZE */ 1 FROM all_objects WHERE rownum &lt;= 1000)
SELECT rownum, lpad(rownum, 10, '0') FROM tmp1, tmp1 WHERE rownum &lt;= 100000
/
COMMIT;

BEGIN
  FOR i IN 1..10 LOOP
    INSERT /*+ APPEND */ INTO t2
      --skipping some data from t1
      SELECT * FROM t1  WHERE NOT(t1.filter1 BETWEEN '0000000051' AND '0000000100');
    COMMIT;
  END LOOP;

  FOR i IN 1..2 LOOP
    INSERT /*+ APPEND */ INTO t3
      SELECT * FROM t1;
    COMMIT;
  END LOOP;  
END;
/

CREATE INDEX t1_i1 ON t1(filter1);
CREATE INDEX t2_i1 ON t2(join2);
CREATE INDEX t3_i1 ON t3(join3);

exec dbms_stats.gather_table_stats(user, 'T1', method_opt =&gt; 'FOR ALL COLUMNS SIZE 1');
exec dbms_stats.gather_table_stats(user, 'T2', method_opt =&gt; 'FOR ALL COLUMNS SIZE 1');
exec dbms_stats.gather_table_stats(user, 'T3', method_opt =&gt; 'FOR ALL COLUMNS SIZE 1');

-- changing view definition is not allowed, again (black box application)
CREATE OR REPLACE VIEW v AS
SELECT t2.join2, t2.filter2, t3.join3, t3.filter3
  FROM t2, t3
 WHERE t2.join2 = t3.join3
/

--parameters to reproduce execution plan
ALTER SESSION SET &quot;_optimizer_cost_model&quot; = io;
ALTER SESSION SET db_file_multiblock_read_count = 8;
ALTER SESSION SET work_area_size_policy=MANUAL;
ALTER SESSION SET hash_area_size=131072;

SELECT * FROM t1, v
WHERE t1.join1 = v.join2
  AND t1.join1 = v.join3
  AND t1.filter1 BETWEEN '0000000050' AND '0000000100'
/

</pre>
<p>The first oddity is that optimizer on my 10.2.0.5 database is not able to use transitive rule int this simple case, that&#8217;s why both predicates have been used (t1.join1 = v.join2 AND  t1.join1 = v.join3) . The query plan for this query (hope to be same on most 10g databases)</p>
<pre class="brush: css; title: ; notranslate">

------------------------------------------------------------------------
| Id  | Operation                      | Name  | Rows  | Bytes | Cost  |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |       |     1 |    45 |   210 |
|   1 |  TABLE ACCESS BY INDEX ROWID   | T2    |     1 |    15 |     1 |
|   2 |   NESTED LOOPS                 |       |     1 |    45 |   210 |
|   3 |    NESTED LOOPS                |       |   104 |  3120 |   107 |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1    |    52 |   780 |     3 |
|*  5 |      INDEX RANGE SCAN          | T1_I1 |    52 |       |     2 |
|   6 |     TABLE ACCESS BY INDEX ROWID| T3    |     2 |    30 |     2 |
|*  7 |      INDEX RANGE SCAN          | T3_I1 |     2 |       |       |
|*  8 |    INDEX RANGE SCAN            | T2_I1 |     1 |       |       |
------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access(&quot;T1&quot;.&quot;FILTER1&quot;&gt;='0000000050' AND
              &quot;T1&quot;.&quot;FILTER1&quot;&lt;='0000000100')
   7 - access(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)
   8 - access(&quot;T2&quot;.&quot;JOIN2&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)
       filter(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T2&quot;.&quot;JOIN2&quot;)

</pre>
<p>The optimizer does not have many opportunities to discover the little &#8220;hole&#8221; in table2, even with histograms on table t2. But the correct join order should be t1 -&gt; t2 -&gt; t3 .<br />
Since only tables at the &#8220;same level&#8221; in FROM clause should be used in LEADING HINT, you are not able to change the join order between tables t2 and t3.</p>
<pre class="brush: css; title: ; notranslate">
SELECT /*+ LEADING(t1 v.t2 v.t3)
           USE_NL(v.t2) INDEX(v.t2)
           USE_NL(v.t3) INDEX(v.t3) */
--leading HINT ignored, not correct use
   *
 FROM t1, v
WHERE t1.join1 = v.join2
  AND t1.join1 = v.join3
  AND t1.filter1 BETWEEN '0000000050' AND '0000000100'
/
</pre>
<p>Unfortunately, the NO_MERGE hint on view would make it even worse, it would prevent from pushing the join predicate into VIEW. When NO_MERGE used, I&#8217;m unable to use any further documented hint to make things work. Here is my best try, but no success. </p>
<p>Note the INDEX FULL SCAN on line 8 making execution plan suboptimal. </p>
<pre class="brush: css; title: ; notranslate">
SELECT /*+ NO_MERGE(v) PUSH_PRED(v)
           LEADING(t1 v) USE_NL(v)
           LEADING(v.t2 v.t3)
           INDEX(v.t2) 
           USE_NL(v.t3) INDEX(v.t2)
       */
   *
 FROM t1, v
WHERE t1.join1 = v.join2
  AND t1.join1 = v.join3
  AND t1.filter1 BETWEEN '0000000050' AND '0000000100'
/ 


-------------------------------------------------------------------------
| Id  | Operation                       | Name  | Rows  | Bytes | Cost  |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |       |  1034 | 77550 |   155M|
|   1 |  NESTED LOOPS                   |       |  1034 | 77550 |   155M|
|   2 |   TABLE ACCESS BY INDEX ROWID   | T1    |    52 |   780 |     3 |
|*  3 |    INDEX RANGE SCAN             | T1_I1 |    52 |       |     2 |
|*  4 |   VIEW                          | V     |    20 |  1200 |  2990K|
|   5 |    TABLE ACCESS BY INDEX ROWID  | T3    |     2 |    30 |     2 |
|   6 |     NESTED LOOPS                |       |  1988K|    56M|  2990K|
|   7 |      TABLE ACCESS BY INDEX ROWID| T2    |   994K|    14M|  1001K|
|   8 |       INDEX FULL SCAN           | T2_I1 |   999K|       |  2222 |
|*  9 |      INDEX RANGE SCAN           | T3_I1 |     2 |       |       |
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access(&quot;T1&quot;.&quot;FILTER1&quot;&gt;='0000000050' AND
              &quot;T1&quot;.&quot;FILTER1&quot;&lt;='0000000100')
   4 - filter(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;V&quot;.&quot;JOIN2&quot; AND &quot;T1&quot;.&quot;JOIN1&quot;=&quot;V&quot;.&quot;JOIN3&quot;)
   9 - access(&quot;T2&quot;.&quot;JOIN2&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)


</pre>
<p>The only way I&#8217;m able to force optimizer choose the best optimal execution plan is using system generated query block names, again.</p>
<pre class="brush: plain; title: ; notranslate">
SELECT
   /*+ LEADING(@SEL$F5BB74E1 t1@SEL$1 t2@SEL$2 t3@SEL$2)
         INDEX(t1)
         USE_NL(v.t2) INDEX(v.t2)
         USE_NL(v.t3) INDEX(v.t3)
   */
     *
 FROM t1, v
WHERE t1.join1 = v.join2
  AND t1.join1 = v.join3
  AND t1.filter1 BETWEEN '0000000050' AND '0000000100'
/

------------------------------------------------------------------------
| Id  | Operation                      | Name  | Rows  | Bytes | Cost  |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |       |     1 |    45 |  1092 |
|   1 |  TABLE ACCESS BY INDEX ROWID   | T3    |     1 |    15 |     1 |
|   2 |   NESTED LOOPS                 |       |     1 |    45 |  1092 |
|   3 |    NESTED LOOPS                |       |   521 | 15630 |   571 |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1    |    52 |   780 |     3 |
|*  5 |      INDEX RANGE SCAN          | T1_I1 |    52 |       |     2 |
|   6 |     TABLE ACCESS BY INDEX ROWID| T2    |    10 |   150 |    11 |
|*  7 |      INDEX RANGE SCAN          | T2_I1 |    10 |       |       |
|*  8 |    INDEX RANGE SCAN            | T3_I1 |     1 |       |       |
------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access(&quot;T1&quot;.&quot;FILTER1&quot;&gt;='0000000050' AND
              &quot;T1&quot;.&quot;FILTER1&quot;&lt;='0000000100')
   7 - access(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T2&quot;.&quot;JOIN2&quot;)
   8 - access(&quot;T2&quot;.&quot;JOIN2&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)
       filter(&quot;T1&quot;.&quot;JOIN1&quot;=&quot;T3&quot;.&quot;JOIN3&quot;)


</pre>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonathan Lewis</title>
		<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/#comment-37909</link>
		<dc:creator><![CDATA[Jonathan Lewis]]></dc:creator>
		<pubDate>Sat, 27 Nov 2010 19:02:51 +0000</pubDate>
		<guid isPermaLink="false">http://jonathanlewis.wordpress.com/?p=4847#comment-37909</guid>
		<description><![CDATA[Pavel,

I think you&#039;re just trying to make things difficult for me.
Do you have a rule against no_merge hints everywhere because of some bad experiences in the past, or is it exactly this type of code where things go wrong ? I&#039;d be interested to see an example of no_merge misbehaviing  (other than in ANSI SQL where virtually everything seems to go wrong with hints.)

Here&#039;s a solution that you probably won&#039;t like and probably shouldn&#039;t use because it takes advantage of an undocumented hint (I wouldn&#039;t use &quot;main&quot; as a name inside a view, by the way; too much risk of the same name being used twice and making Oracle eliminate names in complex code):

[sourcecode gutter=&quot;false&quot;]
select 
	/*+
		outline_leaf(@ind1)
		outline_leaf(@ind2)
		outline_leaf(@ind3)
		outline_leaf(@main)
		leading(@main v1@main v3@main v2@main)
		use_hash(@main v2@main)    swap_join_inputs(@main v2@main)  
		use_hash(@main v3@main) no_swap_join_inputs(@main v3@main)  
	*/
	count(row_id) 
from 
	indmain
;
[/sourcecode]]]></description>
		<content:encoded><![CDATA[<p>Pavel,</p>
<p>I think you&#8217;re just trying to make things difficult for me.<br />
Do you have a rule against no_merge hints everywhere because of some bad experiences in the past, or is it exactly this type of code where things go wrong ? I&#8217;d be interested to see an example of no_merge misbehaviing  (other than in ANSI SQL where virtually everything seems to go wrong with hints.)</p>
<p>Here&#8217;s a solution that you probably won&#8217;t like and probably shouldn&#8217;t use because it takes advantage of an undocumented hint (I wouldn&#8217;t use &#8220;main&#8221; as a name inside a view, by the way; too much risk of the same name being used twice and making Oracle eliminate names in complex code):</p>
<pre class="brush: plain; gutter: false; title: ; notranslate">
select 
	/*+
		outline_leaf(@ind1)
		outline_leaf(@ind2)
		outline_leaf(@ind3)
		outline_leaf(@main)
		leading(@main v1@main v3@main v2@main)
		use_hash(@main v2@main)    swap_join_inputs(@main v2@main)  
		use_hash(@main v3@main) no_swap_join_inputs(@main v3@main)  
	*/
	count(row_id) 
from 
	indmain
;
</pre>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonathan Lewis</title>
		<link>http://jonathanlewis.wordpress.com/2010/11/26/index-join-2/#comment-37908</link>
		<dc:creator><![CDATA[Jonathan Lewis]]></dc:creator>
		<pubDate>Sat, 27 Nov 2010 18:57:47 +0000</pubDate>
		<guid isPermaLink="false">http://jonathanlewis.wordpress.com/?p=4847#comment-37908</guid>
		<description><![CDATA[Timur,

Thanks for checking the newer version.
As Pavol suggests, I wasn&#039;t expecting the buffer visits to be different, it&#039;s simply the execution of the extra predicate that appeared to be using unnecessary CPU. I&#039;ve come across a few cases where generic strategies seem to do redundant work (or miss the obvious) in one version of Oracle, then gradually eliminate the redundancies as time passes. This looks like another example of that development process.  

Nice thought about the cardinality feedback.]]></description>
		<content:encoded><![CDATA[<p>Timur,</p>
<p>Thanks for checking the newer version.<br />
As Pavol suggests, I wasn&#8217;t expecting the buffer visits to be different, it&#8217;s simply the execution of the extra predicate that appeared to be using unnecessary CPU. I&#8217;ve come across a few cases where generic strategies seem to do redundant work (or miss the obvious) in one version of Oracle, then gradually eliminate the redundancies as time passes. This looks like another example of that development process.  </p>
<p>Nice thought about the cardinality feedback.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
