<?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/"
	>

<channel>
	<title>Striving for Optimal PerformanceStriving for Optimal Performance &#187; Query Optimizer</title>
	<atom:link href="http://www.antognini.ch/category/oracledatabase/queryoptimizer/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.antognini.ch</link>
	<description></description>
	<lastBuildDate>Fri, 18 May 2012 11:35:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Index Scan with Filter Predicate Based on a Subquery</title>
		<link>http://www.antognini.ch/2012/02/index-scan-with-filter-predicate-based-on-a-subquery/</link>
		<comments>http://www.antognini.ch/2012/02/index-scan-with-filter-predicate-based-on-a-subquery/#comments</comments>
		<pubDate>Mon, 06 Feb 2012 21:36:58 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[TOP]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1705</guid>
		<description><![CDATA[Most execution plans can be interpreted by following few basic rules (in TOP, Chapter 6, I provide such a list of rules). Nevertheless, there are some special cases. One of them is when an index scan, in addition to the access predicate, has a filter predicate applying a subquery. The following execution plan, taken from [...]]]></description>
			<content:encoded><![CDATA[<p>Most execution plans can be interpreted by following few basic rules (in TOP, Chapter 6, I provide such a list of rules). Nevertheless, there are some special cases. One of them is when an index scan, in addition to the access predicate, has a filter predicate applying a subquery.</p>
<p>The following execution plan, taken from Enterprise Manager 11.2, is an example (click on the image to increase its size):<br />
<a href="/images/ep20120206.jpg" rel="lightbox" title="Index Scan with Filter Predicate Based on a Subquery"><img id="exeplan" src="/images/ep20120206.jpg" alt="Execution Plan" width="550px" /></a><br />
Notes:</p>
<ul>
<li>According to the order column the first operation being executed is the scan of the <code>I2</code> index. Unfortunately this is wrong. In fact the first operation being executed is the scan of the <code>I1</code> index. This is a bug in Enterprise Manager. I wanted to show you this example to demonstrate that not only for us it might be difficult to correctly interpret an execution plan <img src='http://www.antognini.ch/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </li>
<li>The filter predicate <code>IS NOT NULL</code> is also wrong. This is not a bug, however. It is a limitation in the current implementation. The problem is that in some cases the <code>V$SQL_PLAN</code> and <code>V$SQL_PLAN_STATISTICS_ALL</code> views are not able to show all the necessary details.</li>
</ul>
<p>Without seeing the query on which this execution plan is based, it is not obvious at all to know what’s going on. So, here is the query:</p>
<pre>SELECT *
FROM t1
WHERE n1 = 8
AND n2 IN (SELECT t2.n1 FROM t2, t3 WHERE t2.id = t3.id AND t3.n1 = <img src='http://www.antognini.ch/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> </pre>
<p>Based on the query it is essential to point out that the access predicate <code>"T2"."N1"=:B1</code> cannot be evaluated and, therefore, the scan of the <code>I2</code> index cannot be carried out, without having a value passed through the B1 bind variable. In other words, without knowing the value of <code>T1.N2</code>.</p>
<p>To describe how this execution plan is carried out, let’s have a look to the information provided by the <code>DBMS_XPLAN.DISPLAY</code> function (which does not expose the limitation related to the filter predicate).</p>
<pre>-----------------------------------------------
| Id  | Operation                      | Name |
-----------------------------------------------
|   0 | SELECT STATEMENT               |      |
|   1 |  TABLE ACCESS BY INDEX ROWID   | T1   |
|*  2 |   INDEX RANGE SCAN             | I1   |
|   3 |    NESTED LOOPS                |      |
|   4 |     TABLE ACCESS BY INDEX ROWID| T2   |
|*  5 |      INDEX RANGE SCAN          | I2   |
|*  6 |     TABLE ACCESS BY INDEX ROWID| T3   |
|*  7 |      INDEX RANGE SCAN          | I3   |
-----------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("N1"=8)
    filter( EXISTS (SELECT /*+ LEADING ("T2" "T3") USE_NL ("T3") INDEX
           ("T3" "I3") INDEX_RS_ASC ("T2" "I2") */ 0 FROM "T3" "T3","T2" "T2" WHERE
           "T2"."N1"=:B1 AND "T3"."N1"=8 AND "T2"."ID"="T3"."ID"))
5 - access("T2"."N1"=:B1)
6 - filter("T2"."ID"="T3"."ID")
7 - access("T3"."N1"=8)</pre>
<p>The operations are carried out as follows:</p>
<ol>
<li>Operation 2 applies the access predicate <code>"N1"=8</code> by scanning the <code>I1</code> index.</li>
<li>For each key returned by the previous scan, the subquery is executed once. Note that the subquery carries out a nested loop. While the outer loop accesses the <code>T2</code> table, the inner loop accesses the <code>T3</code> table. </li>
<li>The first operation of the outer loop is operation 5. It applies the access predicate <code>"T2"."N1"=:B1</code> by scanning the <code>I2</code> index. Based on the rowid returned by the index access the <code>T2</code> table is accessed (operation 4).</li>
<li>For each row returned by the outer loop, the inner loop is executed once. The first operation of the inner loop is operation 7. It applies the access predicate <code>"T3"."N1"=8</code> by scanning the <code>I3</code> index. Based on the rowid returned by the index access the <code>T3</code> table is accessed (operation 6) and the filter predicate <code>"T2"."ID"="T3"."ID"</code> (the join condition) is applied. By the way, it is interesting to notice that, contrary to the join condition is <em>not</em> applied as an access predicate, as it usually happens.</li>
<li>If the subquery returns a row, the rowid returned by operation 2 can be used to access the <code>T1</code> table (operation 1). The row extracted from this operation is sent to the caller.</li>
</ol>
<p>All in all, this is a very special execution plan&#8230;</p>
<p>In summary, be careful when you see an index scan with a filter predicate applying a subquery. The execution plan might not be carried out as you expect at first sight. It is also essential to point out that in such a case the predicate information is essential to fully understand what’s going on.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2012/02/index-scan-with-filter-predicate-based-on-a-subquery/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Ad: The Oracle Query Optimizer 2-Day Seminar</title>
		<link>http://www.antognini.ch/2011/12/ad-the-oracle-query-optimizer-two-day-seminar/</link>
		<comments>http://www.antognini.ch/2011/12/ad-the-oracle-query-optimizer-two-day-seminar/#comments</comments>
		<pubDate>Sat, 17 Dec 2011 23:42:45 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[Speaking]]></category>
		<category><![CDATA[TOP]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1689</guid>
		<description><![CDATA[The 31st of January and 1st of February 2012 I will present a 2-day seminar about the Oracle query optimizer in Ballerup (DK). The event is organized by Miracle A/S. The content, which is based on the chapters 2, 4, 5, 6, 9 and 10 of my book, is the following: Chapter 1 describes the [...]]]></description>
			<content:encoded><![CDATA[<p>The 31st of January and 1st of February 2012 I will present a 2-day seminar about the Oracle query optimizer in Ballerup (DK). The event is organized by <a href="http://www.miracleas.dk/">Miracle A/S</a>. The content, which is based on the chapters 2, 4, 5, 6, 9 and 10 of <a href="/top">my book</a>, is the following:</p>
<ul>
<li>Chapter 1 describes the life cycle of SQL statements and when the database engine can share cursors.</li>
<li>Chapter 2 describes the aim and architecture of the query optimizer.</li>
<li>Chapter 3 and 4 discuss the statistics used by the query optimizer to carry out its work.</li>
<li>Chapter 5 describes the initialization parameters influencing the behavior of the query optimizer and how to set them.</li>
<li>Chapter 6 outlines different methods of obtaining execution plans, as well as how to read them and recognize inefficient ones.</li>
<li>Chapter 7 describes how to take advantage of available access structures in order to access data stored in a single table efficiently.</li>
<li>Chapter 8 goes beyond accessing a single table, by describing how to join data from several tables together.</li>
</ul>
<p>The <a href="http://antognini.ch/downloads/Miracle_TheOracleQueryOptimizer_20110131.pdf">flyer</a> and this page provide detailed information about the seminar.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/12/ad-the-oracle-query-optimizer-two-day-seminar/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Challenges and Chances of the 11g Query Optimizer</title>
		<link>http://www.antognini.ch/2011/12/challenges-and-chances-of-the-11g-query-optimizer/</link>
		<comments>http://www.antognini.ch/2011/12/challenges-and-chances-of-the-11g-query-optimizer/#comments</comments>
		<pubDate>Mon, 12 Dec 2011 09:59:52 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[Indexes]]></category>
		<category><![CDATA[Object Statistics]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[Speaking]]></category>
		<category><![CDATA[System Statistics]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1665</guid>
		<description><![CDATA[Challenges and Chances of the 11g Query Optimizer is the name of a presentation I gave at several events (e.g. Trivadis Performance Days, Oracle OpenWorld, DOAG Konferenz, UKOUG Conference) throughout 2011. Its abstract is the following: With every new release, the query optimizer is enhanced. Oracle Database 11g Release 1 and Release 2 are no [...]]]></description>
			<content:encoded><![CDATA[<p><em>Challenges and Chances of the 11g Query Optimizer</em> is the name of a presentation I gave at several events (e.g. Trivadis Performance Days, Oracle OpenWorld, DOAG Konferenz, UKOUG Conference) throughout 2011. Its abstract is the following:</p>
<blockquote><p>With every new release, the query optimizer is enhanced. Oracle Database 11g Release 1 and Release 2 are no exception to the rule. Specifically, they introduce key improvements in the following areas: indexing, optimization techniques, object statistics and plan stability. The aim of this presentation is to review the new features from a practical point of view as well as to point out challenges related to them. In other words, to let you know what you can expect from the query optimizer when you upgrade to Oracle Database 11g.</p></blockquote>
<p>The aim of this short post is to point out that I made available the current version of the slides and all the scripts that go with them <a href="/publications/">here</a>.</p>
<p>The structure of the presentation (incl. a reference to the available scripts) is the following:</p>
<ul>
<li>Observations
<ul>
<li>Number of Query Optimizer Parameters by Release</li>
<li>Number of Query Optimizer Bugs Fixed by Patchset</li>
</ul>
</li>
<li>Indexing
<ul>
<li>Invisible Indexes (ex_invisible_index.sql)</li>
<li>Index Support for Linguistic LIKE (ex_linguistic_like.sql)</li>
<li>INDEX REBUILD and Statistics History (ex_index_rebuild.sql)</li>
</ul>
</li>
<li>Optimization Techniques
<ul>
<li>Full Outer Join (ex_full_outer_join.sql)</li>
<li>Join-Filter Pruning (ex_join_filter_pruning.sql)</li>
<li>Table Expansion (ex_table_expansion.sql)</li>
<li>Join Factorization (ex_join_factorization.sql)</li>
<li>OR Expansion (ex_or_expansion.sql)</li>
<li>Join Elimination (ex_join_elimination.sql)</li>
<li>Subquery Unnesting (ex_subquery_unnesting.sql)</li>
</ul>
</li>
<li>System and Object Statistics (DBMS_STATS)
<ul>
<li>Workload System Statistics</li>
<li>Object Statistics – Default Preferences</li>
<li>Object Statistics – Auto Sample Size</li>
<li>Object Statistics – Pending Statistics (ex_pending_object_statistics.sql)</li>
<li>Object Statistics – Incremental Statistics (ex_incremental_stats.sql)</li>
<li>Object Statistics – Extended Statistics on Expressions (ex_extended_statistics1.sql)</li>
<li>Object Statistics – Extended Statistics on Column Groups (ex_extended_statistics2.sql)</li>
<li>Object Statistics – Seeding Column Groups</li>
<li>Object Statistics – Comparing Statistics (ex_comparing_statistics.sql)</li>
<li>Object Statistics – Locks not Exported</li>
<li>JOB_QUEUE_PROCESSES</li>
</ul>
</li>
<li>Plan Stability
<ul>
<li>CURSOR_SHARING</li>
<li>SQL Plan Baselines  (ex_execution_plan_stability.sql, ex_execution_plan_stability_10g.sql, ex_execution_plan_stability_11g.sql)</li>
<li>Stored Outlines</li>
<li>Adaptive Cursor Sharing (ex_bind_peeking.sql, ex_bind_peeking_bind_aware.sql)</li>
<li>Cardinality Feedback (ex_cardinality_feedback.sql)</li>
</ul>
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/12/challenges-and-chances-of-the-11g-query-optimizer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Impact of STATISTICS_LEVEL on Cardinality Feedback and Adaptive Cursor Sharing</title>
		<link>http://www.antognini.ch/2011/09/impact-of-statistics_level-on-cardinality-feedback-and-adaptive-cursor-sharing/</link>
		<comments>http://www.antognini.ch/2011/09/impact-of-statistics_level-on-cardinality-feedback-and-adaptive-cursor-sharing/#comments</comments>
		<pubDate>Mon, 19 Sep 2011 17:20:09 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Query Optimizer]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1572</guid>
		<description><![CDATA[The STATISTICS_LEVEL parameter controls a bunch of features. In addition to the documentation, also the V$STATISTICS_LEVEL view provides a list of the ones it controls. SQL> SELECT statistics_name, description, activation_level 2 FROM v$statistics_level 3 ORDER BY 3 DESC, 1; STATISTICS_NAME DESCRIPTION ACTIVATION_LEVEL -------------------------------------- ------------------------------------------------------------ ---------------- Active Session History Monitors active session activity using MMNL TYPICAL [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e17110/initparams249.htm#CHDDAHJD">STATISTICS_LEVEL</a> parameter controls a bunch of features. In addition to the <a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e17110/initparams249.htm#CHDDAHJD">documentation</a>, also the <a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e17110/dynviews_3075.htm#I1030264">V$STATISTICS_LEVEL</a> view provides a list of the ones it controls.</p>
<pre>SQL> SELECT statistics_name, description, activation_level
  2  FROM v$statistics_level
  3  ORDER BY 3 DESC, 1;

STATISTICS_NAME                        DESCRIPTION                                                  ACTIVATION_LEVEL
-------------------------------------- ------------------------------------------------------------ ----------------
Active Session History                 Monitors active session activity using MMNL                  TYPICAL
Adaptive Thresholds Enabled            Controls if Adaptive Thresholds should be enabled            TYPICAL
Automated Maintenance Tasks            Controls if Automated Maintenance should be enabled          TYPICAL
Bind Data Capture                      Enables capture of bind values used by SQL statements        TYPICAL
Buffer Cache Advice                    Predicts the impact of different cache sizes on number of    TYPICAL
                                       physical reads
Global Cache Statistics                RAC Buffer Cache statistics                                  TYPICAL
Longops Statistics                     Enables Longops Statistics                                   TYPICAL
MTTR Advice                            Predicts the impact of different MTTR settings on number of  TYPICAL
                                       physical I/Os
Modification Monitoring                Enables modification monitoring                              TYPICAL
PGA Advice                             Predicts the impact of different values of pga_aggregate_tar TYPICAL
                                       get on the performance of memory intensive SQL operators
Plan Execution Sampling                Enables plan lines sampling                                  TYPICAL
SQL Monitoring                         Controls if SQL Monitoring should be enabled                 TYPICAL
Segment Level Statistics               Enables gathering of segment access statistics               TYPICAL
Shared Pool Advice                     Predicts the impact of different values of shared_pool_size  TYPICAL
                                       on elapsed parse time saved
Streams Pool Advice                    Predicts impact on Streams perfomance of different  Streams  TYPICAL
                                       pool sizes
Threshold-based Alerts                 Controls if Threshold-based Alerts should be enabled         TYPICAL
Time Model Events                      Enables Statics collection for time events                   TYPICAL
Timed Statistics                       Enables gathering of timed statistics                        TYPICAL
Ultrafast Latch Statistics             Maintains statistics for ultrafast latches in the fast path  TYPICAL
Undo Advisor, Alerts and Fast Ramp up  Transaction layer manageability features                     TYPICAL
V$IOSTAT_* statistics                  Controls if I/O stats in v$iostat_ should be enabled         TYPICAL
Plan Execution Statistics              Enables collection of plan execution statistics              ALL
Timed OS Statistics                    Enables gathering of timed operating system statistics       ALL
</pre>
<p>Something that I learned only recently is that STATISTICS_LEVEL also controls <em>cardinality feedback</em> and <em>adaptive cursor sharing</em>. This fact, according to me, is neither (clearly) documented nor pointed out by the information provided by V$STATISTICS_LEVEL. In any case, when STATISTICS_LEVEL is set to BASIC at the <em>system level</em> both features are disabled. Interestingly, an ALTER SESSION SET STATISTICS_LEVEL = TYPICAL it is not enough to enable them… For adaptive cursor sharing it is possible to use the BIND_AWARE hint, though.</p>
<p>Note that I never advise to set STATISTICS_LEVEL at the system level to a value that is different from the default (TYPICAL). Probably for this reason I didn’t notice its impact for such a long time…</p>
<p>In any case I find it a bit disappointing that this information is not clearly stated somewhere. Or I’m the only one that was not aware?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/09/impact-of-statistics_level-on-cardinality-feedback-and-adaptive-cursor-sharing/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>optimizer_secure_view_merging and VPD</title>
		<link>http://www.antognini.ch/2011/09/optimizer_secure_view_merging-and-vpd/</link>
		<comments>http://www.antognini.ch/2011/09/optimizer_secure_view_merging-and-vpd/#comments</comments>
		<pubDate>Sun, 11 Sep 2011 09:21:49 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[TOP]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1552</guid>
		<description><![CDATA[At page 189 of TOP I wrote the following piece of text: In summary, with the initialization parameter optimizer_secure_view_merging set to TRUE, the query optimizer checks whether view merging could lead to security issues. If this is the case, no view merging will be performed, and performance could be suboptimal as a result. For this [...]]]></description>
			<content:encoded><![CDATA[<p>At page 189 of <a href="/top">TOP</a> I wrote the following piece of text:</p>
<blockquote><p>In summary, with the initialization parameter optimizer_secure_view_merging set to TRUE, the query optimizer checks whether view merging could lead to security issues. If this is the case, no view merging will be performed, and performance could be suboptimal as a result. For this reason, if you are not using views for security purposes, it is better to set this initialization parameter to FALSE.</p></blockquote>
<p>What I didn’t consider when I wrote it, it is the implication of predicate move-around related to Virtual Private Database (VPD). In fact, as described in the <a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e17110/initparams168.htm#I1010262">documentation</a>, that parameter controls <em>view merging</em> as well as <em>predicate move-around</em>.</p>
<p>To point out what the impact is, let’s have a look to an example based on the description provided in <a href="/top">TOP</a>:</p>
<ul>
<li>Say you have a very simple table with one primary key and two more columns.</li>
</ul>
<pre>CREATE TABLE t (
  id NUMBER(10) PRIMARY KEY,
  class NUMBER(10),
  pad VARCHAR2(10)
);</pre>
<ul>
<li>For security reasons, you define the following policy. Notice the filter that is applied with the function to partially show the content of the table. How this function is implemented and what it does exactly is not important.</li>
</ul>
<pre>CREATE OR REPLACE FUNCTION s (schema IN VARCHAR2, tab IN VARCHAR2) RETURN VARCHAR2 AS
BEGIN
  RETURN 'f(class) = 1';
END;
/

BEGIN
  dbms_rls.add_policy(object_schema   => 'U1',
                      object_name     => 'T',
                      policy_name     => 'T_SEC',
                      function_schema => 'U1',
                      policy_function => 'S');
END;
/</pre>
<ul>
<li>Now let’s say that a user who has access to the table creates the following PL/SQL function. As you can see, it will just display the value of the input parameters through a call to the package dbms_output.</li>
</ul>
<pre>CREATE OR REPLACE FUNCTION spy (id IN NUMBER, pad IN VARCHAR2) RETURN NUMBER AS
BEGIN
  dbms_output.put_line('id=' || id || ' pad=' || pad);
  RETURN 1;
END;
/</pre>
<ul>
<li>With the initialization parameter optimizer_secure_view_merging set to FALSE, you can run two test queries. Both return only the values that the user is allowed to see. In the second one, however, you are able to see data that you should not be able to access.</li>
</ul>
<pre>SQL> SELECT id, pad
  2  FROM t
  3  WHERE id BETWEEN 1 AND 5;

        ID PAD
---------- ----------
         1 DrMLTDXxxq
         4 AszBGEUGEL

SQL> SELECT id, pad
  2  FROM t
  3  WHERE id BETWEEN 1 AND 5
  4  AND spy(id, pad) = 1;

        ID PAD
---------- ----------
         1 DrMLTDXxxq
         4 AszBGEUGEL
id=1 pad=DrMLTDXxxq
id=2 pad=XOZnqYRJwI
id=3 pad=nlGfGBTxNk
id=4 pad=AszBGEUGEL
id=5 pad=qTSRnFjRGb</pre>
<ul>
<li>With the initialization parameter optimizer_secure_view_merging set to TRUE, the second query returns the following output. As you can see, the function and the query display the same data.</li>
</ul>
<pre>SQL> SELECT id, pad
  2  FROM t
  3  WHERE id BETWEEN 1 AND 5
  4  AND spy(id, pad) = 1;

        ID PAD
---------- ----------
         1 DrMLTDXxxq
         4 AszBGEUGEL
id=1 pad=DrMLTDXxxq
id=4 pad=AszBGEUGEL</pre>
<p>The execution plans that are used in the two situations are the following. As you can see only the second one guarantee that the policy defined via VPD is applied before the predicate based on the SPY function. Interestingly enough the other predicate based on the ID column is applied before the one of the policy. Hence, the query optimizer can choose an access path that takes advantage of the primary key.</p>
<ul>
<li>optimizer_secure_view_merging = FALSE</li>
</ul>
<pre>---------------------------------------------------
| Id  | Operation                   | Name        |
---------------------------------------------------
|   0 | SELECT STATEMENT            |             |
|*  1 |  TABLE ACCESS BY INDEX ROWID| T           |
|*  2 |   INDEX RANGE SCAN          | SYS_C009970 |
---------------------------------------------------

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

   1 - filter(("SPY"("ID","PAD")=1 AND "F"("CLASS")=1))
   2 - access("ID">=1 AND "ID"<=5)</pre>
<ul>
<li>optimizer_secure_view_merging = TRUE</li>
</ul>
<pre>----------------------------------------------------
| Id  | Operation                    | Name        |
----------------------------------------------------
|   0 | SELECT STATEMENT             |             |
|*  1 |  VIEW                        | T           |
|*  2 |   TABLE ACCESS BY INDEX ROWID| T           |
|*  3 |    INDEX RANGE SCAN          | SYS_C009971 |
----------------------------------------------------

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

   1 - filter("SPY"("ID","PAD")=1)
   2 - filter("F"("CLASS")=1)
   3 - access("ID">=1 AND "ID"<=5)</pre>
<p>Based on these observations, the summary that is provided by <a href="/top">TOP</a> at page 189 should be amended as follows:</p>
<blockquote><p>In summary, with the initialization parameter optimizer_secure_view_merging set to TRUE, the query optimizer checks whether view merging or predicate move-around could lead to security issues. If this is the case, they will not be performed, and performance could be suboptimal as a result. For this reason, if you are not using views or VPD for security purposes, it is better to set this initialization parameter to FALSE.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/09/optimizer_secure_view_merging-and-vpd/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>IS NULL Conditions and B-tree Indexes</title>
		<link>http://www.antognini.ch/2011/02/is-null-conditions-and-b-tree-indexes/</link>
		<comments>http://www.antognini.ch/2011/02/is-null-conditions-and-b-tree-indexes/#comments</comments>
		<pubDate>Thu, 17 Feb 2011 10:01:56 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR1]]></category>
		<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[9iR2]]></category>
		<category><![CDATA[Indexes]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[TOP]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1479</guid>
		<description><![CDATA[At page 383 of my book I wrote the following sentence (BTW, the same information is also provided by Table 9-3 at page 381): With B-tree indexes, IS NULL conditions can be applied only through composite B-tree indexes when several SQL conditions are applied and at least one of them is not based on IS [...]]]></description>
			<content:encoded><![CDATA[<p>At page 383 of <a href="/top">my book</a> I wrote the following sentence (BTW, the same information is also provided by Table 9-3 at page 381):</p>
<blockquote><p>With B-tree indexes, IS NULL conditions can be applied only through composite B-tree indexes when several SQL conditions are applied and at least one of them is not based on IS NULL or an inequality.</p></blockquote>
<p>The text continues by showing the following examples (notice that in both cases the IS NULL predicate is applied through an access predicate):</p>
<pre>SELECT /*+ index(t) */ * FROM t WHERE n1 = 6 AND n2 IS NULL

Plan hash value: 780655320

----------------------------------------------
| Id  | Operation                   | Name   |
----------------------------------------------
|   0 | SELECT STATEMENT            |        |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |
|*  2 |   INDEX RANGE SCAN          | I_N123 |
----------------------------------------------

   2 - access("N1"=6 AND "N2" IS NULL)

SELECT /*+ index(t) */ * FROM t WHERE n1 IS NULL AND n2 = 8

Plan hash value: 780655320

----------------------------------------------
| Id  | Operation                   | Name   |
----------------------------------------------
|   0 | SELECT STATEMENT            |        |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |
|*  2 |   INDEX RANGE SCAN          | I_N123 |
----------------------------------------------

   2 - access("N1" IS NULL AND "N2"=8)
       filter("N2"=8)</pre>
<p>When I wrote that sentence I didn&#8217;t think about one case that, according to it, specifically the part &#8220;is not based on IS NULL or an inequality&#8221;, is not covered. In fact, as the following examples show, it is also possible to apply an IS NULL predicate when the other one is an IS NOT NULL. It is especially interesting to notice that the access predicate doesn&#8217;t reference at all the NOT NULL column!</p>
<pre>SELECT /*+ index(t) */ * FROM t WHERE n1 IS NULL AND n2 IS NOT NULL

Plan hash value: 780655320

----------------------------------------------
| Id  | Operation                   | Name   |
----------------------------------------------
|   0 | SELECT STATEMENT            |        |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |
|*  2 |   INDEX RANGE SCAN          | I_N123 |
----------------------------------------------

   2 - access("N1" IS NULL)
       filter("N2" IS NOT NULL)

SELECT /*+ index(t) */ * FROM t WHERE n1 IS NOT NULL AND n2 IS NULL

Plan hash value: 3029444779

----------------------------------------------
| Id  | Operation                   | Name   |
----------------------------------------------
|   0 | SELECT STATEMENT            |        |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |
|*  2 |   INDEX SKIP SCAN           | I_N123 |
----------------------------------------------

   2 - access("N2" IS NULL)
       filter(("N2" IS NULL AND "N1" IS NOT NULL))</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2011/02/is-null-conditions-and-b-tree-indexes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>DBMS_AUTO_SQLTUNE: ORA-01748 and Documentation Bugs</title>
		<link>http://www.antognini.ch/2010/10/dbms_auto_sqltune-ora-01748-and-documentation-bugs/</link>
		<comments>http://www.antognini.ch/2010/10/dbms_auto_sqltune-ora-01748-and-documentation-bugs/#comments</comments>
		<pubDate>Wed, 06 Oct 2010 15:04:00 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[Query Optimizer]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1328</guid>
		<description><![CDATA[As of 11.2.0.2 a new package, DBMS_AUTO_SQLTUNE, is available for accessing and configuring Automatic SQL Tuning. The package provides three features: Execution of the Automatic SQL Tuning task (EXECUTE_AUTO_TUNING_TASK) Generation of a report showing the output generated by the Automatic SQL Tuning tasks (REPORT_AUTO_TUNING_TASK) Configuration of the Automatic SQL Tuning parameters (SET_AUTO_TUNING_TASK_PARAMETER) In this post [...]]]></description>
			<content:encoded><![CDATA[<p>As of 11.2.0.2 a new package, DBMS_AUTO_SQLTUNE, is available for accessing and configuring Automatic SQL Tuning. The package provides three features:</p>
<ul>
<li>Execution of the Automatic SQL Tuning task (EXECUTE_AUTO_TUNING_TASK)</li>
<li>Generation of a report showing the output generated by the Automatic SQL Tuning tasks (REPORT_AUTO_TUNING_TASK)</li>
<li>Configuration of the Automatic SQL Tuning parameters (SET_AUTO_TUNING_TASK_PARAMETER)</li>
</ul>
<p>In this post I would like to focus on the second functionality. With it you can for example execute the following commands in SQL*Plus to get a report for the most recent task:</p>
<pre>SET LONG 1000000 PAGESIZE 0 LINESIZE 200
COLUMN report_auto_tuning_task FORMAT A200
SELECT dbms_auto_sqltune.report_auto_tuning_task FROM dual;</pre>
<p>The REPORT_AUTO_TUNING_TASK function is not limited to being used without parameters. In fact, its signature is the following:</p>
<pre>FUNCTION REPORT_AUTO_TUNING_TASK RETURNS CLOB
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 BEGIN_EXEC                     VARCHAR2                IN     DEFAULT
 END_EXEC                       VARCHAR2                IN     DEFAULT
 TYPE                           VARCHAR2                IN     DEFAULT
 LEVEL                          VARCHAR2                IN     DEFAULT
 SECTION                        VARCHAR2                IN     DEFAULT
 OBJECT_ID                      NUMBER                  IN     DEFAULT
 RESULT_LIMIT                   NUMBER                  IN     DEFAULT</pre>
<p>The purpose of the parameters is the following:</p>
<ul>
<li>BEGIN_EXEC/END_EXEC specifies which tasks have to be reported. With the default value, NULL, the most recent task is shown.</li>
<li>TYPE specifies the type of the report. Presently only TEXT is supported.</li>
<li>LEVEL specifies the level of detail shown in the report. This is somewhat similar to the FORMAT parameter of DBMS_XPLAN. The supported values are BASIC, TYPICAL (default) and ALL.</li>
<li>SECTION specifies which sections are shown in the report. The supported values are SUMMARY, FINDINGS, PLANS, INFORMATION, ERRORS and ALL (default). Possibly there is a bug with the value ERRORS. In fact, during my tests, it always returned the same report as the value INFORMATION!?!?</li>
<li>OBJECT_ID restricts the report to a single SQL statement. You can get the ID either from the report or by querying DBA_ADVISOR_OBJECTS.OBJECT_ID.</li>
<li>RESULT_LIMIT specifies the maximum number of SQL statements shown in the report.</li>
</ul>
<p>The essential thing I would like to point out is that two parameters have as name a reserved word.</p>
<pre>SQL> SELECT keyword
  2  FROM v$reserved_words
  3  WHERE keyword IN ('BEGIN_EXEC','END_EXEC','TYPE','LEVEL','SECTION','OBJECT_ID','RESULT_LIMIT');

KEYWORD
------------------------------
LEVEL
TYPE</pre>
<p>As a result expect to get an error like the following one if you want to use named notation:</p>
<pre>SELECT dbms_auto_sqltune.report_auto_tuning_task(level => 'basic') FROM dual
                                                       *
ERROR at line 1:
ORA-01748: only simple column names allowed here</pre>
<p>These are the kind of things I cannot understand! It&#8217;s so easy to choose a name that doesn&#8217;t lead to such problems. For one time I hope that Oracle will break backward compatibility and change the name of these parameters.</p>
<p>Another thing I wanted to point out is that the values supported by the SECTION parameter are the ones I reported above, and not the ones documented <a href="http://download.oracle.com/docs/cd/E11882_01/appdev.112/e16760/d_autosta.htm#CIAEHBGA">here</a>. In fact, there are two typos in the documentation. This is probably because the same typos can also be seen in $ORACLE_HOME/rdbms/admin/dbmssqlt.sql. In other words, it seems that the guy who wrote the documentation did a simple copy/paste of the content of the SQL file.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2010/10/dbms_auto_sqltune-ora-01748-and-documentation-bugs/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Exadata Storage Server and the Query Optimizer – Part 4</title>
		<link>http://www.antognini.ch/2010/08/exadata-storage-server-and-the-query-optimizer-part-4/</link>
		<comments>http://www.antognini.ch/2010/08/exadata-storage-server-and-the-query-optimizer-part-4/#comments</comments>
		<pubDate>Mon, 09 Aug 2010 15:37:40 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[Exadata]]></category>
		<category><![CDATA[Query Optimizer]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1227</guid>
		<description><![CDATA[When I started writing the series of posts about Exadata Storage Server and the query optimizer, I didn’t expect to write more than three posts (part 1, part 2, part 3). Of course, things change. Hence, here is part 4 to cover a couple of things that I learned in the next couple of months. [...]]]></description>
			<content:encoded><![CDATA[<p>When I started writing the series of posts about Exadata Storage Server and the query optimizer, I didn’t expect to write more than three posts (<a href="/2010/04/exadata-storage-server-and-the-query-optimizer-part-1/">part 1</a>, <a href="/2010/05/exadata-storage-server-and-the-query-optimizer-part-2/">part 2</a>, <a href="/2010/05/exadata-storage-server-and-the-query-optimizer-part-3/">part 3</a>). Of course, things change. Hence, here is part 4 to cover a couple of things that I learned in the next couple of months.</p>
<p>In <a href="/2010/05/exadata-storage-server-and-the-query-optimizer-part-2/">part 2</a> I pointed out that Oracle Database is not able to offload the processing of all datetime functions. This fact, to my surprise, was also referenced by <a href="http://www.netezza.com/">Netezza</a> in a recent paper entitled <a href="http://www.netezza.com/exadata-twinfin-compared/index.aspx">Oracle Exadata and Netezza TwinFin Compared</a>. The essential thing to understand is that this limitation is due to bug 9682721. The fix is expected to be part of 11.2.0.2. According to my test cases (that <a href="http://structureddata.org/">Greg Rahn</a> was so kind to execute against an early release of 11.2.0.2), offloading works correctly for all datetime functions but for the following three predicates.</p>
<ul>
<li>months_between(d,sysdate) = 0</li>
<li>months_between(d,current_date) = 0</li>
<li>months_between(d,to_date(&#8217;01-01-2010&#8242;,&#8217;DD-MM-YYYY&#8217;)) = 0</li>
</ul>
<p>Note that the MONTHS_BETWEEN function can basically be offloaded. The problem in these cases is that the offloading does not work when, for example, SYSDATE is used as parameter.</p>
<p>To have a full list of the functions supporting offloading, the “official reference” is available through the V$SQLFN_METADATA view. Here is a simple query to summarize the current situation.</p>
<pre>SQL> SELECT offloadable, count(DISTINCT name)
  2  FROM v$sqlfn_metadata
  3  GROUP BY offloadable;

OFF COUNT(DISTINCTNAME)
--- -------------------
NO                  511
YES                 319</pre>
<p>Another thing I would like to point out about offloading is that the feature can be controlled through the CELL_OFFLOAD_PROCESSING initialization parameter. By default it is set to TRUE and, therefore, offloading is used whenever possible. It goes without saying that offloading is disabled when it is set to FALSE. Note that it can not only be set at the instance and session level, but also at the statement level. The following example illustrate this (notice that only the first query uses offloading).</p>
<pre>SQL> ALTER SESSION SET cell_offload_plan_display = always;

SQL> ALTER SESSION SET cell_offload_processing = true;

SQL> EXPLAIN PLAN FOR SELECT * FROM t WHERE id = 101;

SQL> SELECT * FROM table(dbms_xplan.display(format=>'basic +predicate'));

PLAN_TABLE_OUTPUT
---------------------------------------------------
Plan hash value: 3557914527

-------------------------------------------
| Id  | Operation                  | Name |
-------------------------------------------
|   0 | SELECT STATEMENT           |      |
|   1 |  PARTITION RANGE ALL       |      |
|*  2 |   TABLE ACCESS STORAGE FULL| T    |
-------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - storage("ID"=101)
       filter("ID"=101)

SQL> EXPLAIN PLAN FOR SELECT /*+ opt_param('cell_offload_processing' 'false') */  * FROM t WHERE id = 101;

SQL> SELECT * FROM table(dbms_xplan.display(format=>'basic +predicate'));

PLAN_TABLE_OUTPUT
---------------------------------------------------
Plan hash value: 3557914527

-------------------------------------------
| Id  | Operation                  | Name |
-------------------------------------------
|   0 | SELECT STATEMENT           |      |
|   1 |  PARTITION RANGE ALL       |      |
|*  2 |   TABLE ACCESS STORAGE FULL| T    |
-------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("ID"=101)</pre>
<p>Another initialization parameter that controls offloading is CELL_OFFLOAD_DECRYPTION. This parameter is relevant for encrypted tablespaces only. With it you can specify whether the keys necessary to decrypt the data can be shipped to the cells. By default it is set to TRUE and, therefore, the keys are shipped. For security reasons you might want to set it to FALSE and disable offloading for encrypted tablespaces. Note that this parameter can only be changed at the instance level.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2010/08/exadata-storage-server-and-the-query-optimizer-part-4/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Partition-Wise Join of List-Partitioned Tables</title>
		<link>http://www.antognini.ch/2010/08/partition-wise-join-of-list-partitioned-tables/</link>
		<comments>http://www.antognini.ch/2010/08/partition-wise-join-of-list-partitioned-tables/#comments</comments>
		<pubDate>Mon, 02 Aug 2010 09:59:08 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR1]]></category>
		<category><![CDATA[10gR2]]></category>
		<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Partitioning]]></category>
		<category><![CDATA[Query Optimizer]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1202</guid>
		<description><![CDATA[When two tables are equi-partitioned on their join keys, the query optimizer is able to take advantage of partition-wise joins. To make sure that the tables are equi-partitioned, as of Oracle Database 11g reference partitioning can be used. In fact, per definition, with reference partitioning all “related” tables have exactly the same partitioning schema. If [...]]]></description>
			<content:encoded><![CDATA[<p>When two tables are equi-partitioned on their join keys, the query optimizer is able to take advantage of partition-wise joins. To make sure that the tables are equi-partitioned, as of Oracle Database 11g reference partitioning can be used. In fact, per definition, with reference partitioning all “related” tables have exactly the same partitioning schema. If you are not using reference partitioning, you must be very careful that the tables are effectively partitioned in very same way. For range and hash partitioned tables this is usually not a problem. However, when using list partitioning, it is quite easy to make a mistake. The reason is that the partitions can be defined in any order. Let’s have a look to an example based on the following two tables.</p>
<pre>SQL> CREATE TABLE t1p
  2  PARTITION BY LIST (pkey) (
  3    PARTITION p_0 VALUES (0),
  4    PARTITION p_1 VALUES (1),
  5    PARTITION p_2 VALUES (2),
  6    PARTITION p_3 VALUES (3),
  7    PARTITION p_4 VALUES (4),
  8    PARTITION p_5 VALUES (5),
  9    PARTITION p_6 VALUES (6),
 10    PARTITION p_7 VALUES (7),
 11    PARTITION p_8 VALUES (8),
 12    PARTITION p_9 VALUES (9)
 13  )
 14  AS
 15  SELECT rownum AS num, mod(rownum,10) AS pkey, dbms_random.string('p',50) AS pad
 16  FROM dual
 17  CONNECT BY level <= 10000;

SQL> CREATE TABLE t2p
  2  PARTITION BY LIST (pkey) (
  3    PARTITION p_0 VALUES (0),
  4    PARTITION p_1 VALUES (1),
  5    PARTITION p_2 VALUES (2),
  6    PARTITION p_3 VALUES (3),
  7    PARTITION p_5 VALUES (5),
  8    PARTITION p_4 VALUES (4),
  9    PARTITION p_6 VALUES (6),
 10    PARTITION p_7 VALUES (7),
 11    PARTITION p_8 VALUES (8),
 12    PARTITION p_9 VALUES (9)
 13  )
 14  AS
 15  SELECT rownum AS num, mod(rownum,10) AS pkey, dbms_random.string('p',50) AS pad
 16  FROM dual
 17  CONNECT BY level <= 10000;

SQL> BEGIN
  2    dbms_stats.gather_table_stats(user,'t1p');
  3    dbms_stats.gather_table_stats(user,'t2p');
  4  END;
  5  /</pre>
<p>Even though they are logically equivalent, as shown in the following execution plan, with them partition-wise joins cannot be used.</p>
<pre>SQL> EXPLAIN PLAN FOR SELECT * FROM t1p JOIN t2p USING (num, pkey);

SQL> SELECT * FROM table(dbms_xplan.display(format=>'basic'));

PLAN_TABLE_OUTPUT
------------------------------------

Plan hash value: 3059592055

------------------------------------
| Id  | Operation           | Name |
------------------------------------
|   0 | SELECT STATEMENT    |      |
|   1 |  HASH JOIN          |      |
|   2 |   PARTITION LIST ALL|      |
|   3 |    TABLE ACCESS FULL| T1P  |
|   4 |   PARTITION LIST ALL|      |
|   5 |    TABLE ACCESS FULL| T2P  |
------------------------------------</pre>
<p>The difference in the order of the partitions can also be confirmed by a query like the following one.</p>
<pre>SQL> SELECT t1p.high_value,
  2         t1p.partition_position AS pos_t1p,
  3         t2p.partition_position AS pos_t2p,
  4         decode(t1p.partition_position, t2p.partition_position, 'Y', 'N') AS equal
  5  FROM user_tab_partitions t1p JOIN user_tab_partitions t2p ON t1p.partition_name = t2p.partition_name
  6  WHERE t1p.table_name = 'T1P'
  7  AND t2p.table_name = 'T2P';

HIGH_VALUE   POS_T1P  POS_T2P EQUAL
----------- -------- -------- ------
0                  1        1 Y
1                  2        2 Y
2                  3        3 Y
3                  4        4 Y
5                  6        5 N
4                  5        6 N
6                  7        7 Y
7                  8        8 Y
8                  9        9 Y
9                 10       10 Y</pre>
<p>It goes without saying that to solve the problem it is necessary to reorder the partitions. To do so it is enough to move the out-of-order partitions. To avoid a double storage of the data a series of ALTER TABLE EXCHANGE/DROP/ADD/EXCHANGE statements can be used.</p>
<ul>
<li>Move the P5 partition of the T1P table</li>
</ul>
<pre>SQL> CREATE TABLE t1p_5 AS
  2  SELECT *
  3  FROM t1p PARTITION (p_5)
  4  WHERE 1 = 0;

SQL> ALTER TABLE t1p EXCHANGE PARTITION p_5 WITH TABLE t1p_5;

SQL> ALTER TABLE t1p DROP PARTITION p_5;

SQL> ALTER TABLE t1p ADD PARTITION p_5 VALUES (5);

SQL> ALTER TABLE t1p EXCHANGE PARTITION p_5 WITH TABLE t1p_5;

SQL> DROP TABLE t1p_5 PURGE;</pre>
<ul>
<li>Move the P5 partition of the T2P table</li>
</ul>
<pre>SQL> CREATE TABLE t2p_5 AS
  2  SELECT *
  3  FROM t2p PARTITION (p_5)
  4  WHERE 1 = 0;

SQL> ALTER TABLE t2p EXCHANGE PARTITION p_5 WITH TABLE t2p_5;

SQL> ALTER TABLE t2p DROP PARTITION p_5;

SQL> ALTER TABLE t2p ADD PARTITION p_5 VALUES (5);

SQL> ALTER TABLE t2p EXCHANGE PARTITION p_5 WITH TABLE t2p_5;

SQL> DROP TABLE t2p_5 PURGE;</pre>
<ul>
<li>Check whether the order is ok</li>
</ul>
<pre>SQL> SELECT t1p.high_value,
  2         t1p.partition_position AS pos_t1p,
  3         t2p.partition_position AS pos_t2p,
  4         decode(t1p.partition_position, t2p.partition_position, 'Y', 'N') AS equal
  5  FROM user_tab_partitions t1p JOIN user_tab_partitions t2p ON t1p.partition_name = t2p.partition_name
  6  WHERE t1p.table_name = 'T1P'
  7  AND t2p.table_name = 'T2P';

HIGH_VALUE   POS_T1P  POS_T2P EQUAL
----------- -------- -------- ------
0                  1        1 Y
1                  2        2 Y
2                  3        3 Y
3                  4        4 Y
4                  5        5 Y
6                  6        6 Y
7                  7        7 Y
8                  8        8 Y
9                  9        9 Y
5                 10       10 Y</pre>
<p>After these operations partition-wise joins are allowed. The following execution plan confirms this.</p>
<pre>SQL> SELECT * FROM table(dbms_xplan.display(format=>'basic'));

PLAN_TABLE_OUTPUT
------------------------------------

Plan hash value: 1324269388

------------------------------------
| Id  | Operation           | Name |
------------------------------------
|   0 | SELECT STATEMENT    |      |
|   1 |  PARTITION LIST ALL |      |
|   2 |   HASH JOIN         |      |
|   3 |    TABLE ACCESS FULL| T1P  |
|   4 |    TABLE ACCESS FULL| T2P  |
------------------------------------</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2010/08/partition-wise-join-of-list-partitioned-tables/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Related-Combine Operation „UNION ALL (RECURSIVE WITH)“</title>
		<link>http://www.antognini.ch/2010/06/related-combine-operation-union-all-recursive-with/</link>
		<comments>http://www.antognini.ch/2010/06/related-combine-operation-union-all-recursive-with/#comments</comments>
		<pubDate>Thu, 10 Jun 2010 06:53:37 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[TOP]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1160</guid>
		<description><![CDATA[To make easier the interpretation of execution plans, in chapter 6 of TOP I defined three types of operations: standalone operations, unrelated-combine operations, and related-combine operations. For combine operations I also added a list of all operations of each type. Since in 11.2 a new related-combine operation is available, I decided to write this short [...]]]></description>
			<content:encoded><![CDATA[<p>To make easier the interpretation of execution plans, in chapter 6 of <a href="/top">TOP</a> I defined three types of operations: standalone operations, unrelated-combine operations, and related-combine operations. For combine operations I also added a list of all operations of each type. Since in 11.2 a new related-combine operation is available, I decided to write this short post as addenda to the content of the book.</p>
<p>The new related-combine operation, named “UNION ALL (RECURSIVE WITH)”, is available to support the new recursive <a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e10592/statements_10002.htm#i2077142">subquery factoring clause</a>. Hence, it is used for hierarchical queries. The following query and its execution plan show an example:</p>
<pre>SQL> WITH
  2    e (xlevel, empno, ename, job, mgr, hiredate, sal, comm, deptno)
  3    AS (
  4      SELECT 1, empno, ename, job, mgr, hiredate, sal, comm, deptno
  5      FROM emp
  6      WHERE mgr IS NULL
  7      UNION ALL
  8      SELECT mgr.xlevel+1, emp.empno, emp.ename, emp.job, emp.mgr, emp.hiredate, emp.sal, emp.comm, emp.deptno
  9      FROM emp, e mgr
 10      WHERE emp.mgr = mgr.empno
 11    )
 12  SELECT *
 13  FROM e;

-------------------------------------------------------------------------------
| Id  | Operation                                 | Name    | Starts | A-Rows |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                          |         |      1 |     14 |
|   1 |  VIEW                                     |         |      1 |     14 |
|   2 |   UNION ALL (RECURSIVE WITH) BREADTH FIRST|         |      1 |     14 |
|*  3 |    TABLE ACCESS FULL                      | EMP     |      1 |      1 |
|   4 |    NESTED LOOPS                           |         |      4 |     13 |
|   5 |     NESTED LOOPS                          |         |      4 |     13 |
|   6 |      RECURSIVE WITH PUMP                  |         |      4 |     14 |
|*  7 |      INDEX RANGE SCAN                     | EMP_MGR |     14 |     13 |
|   8 |     TABLE ACCESS BY INDEX ROWID           | EMP     |     13 |     13 |
-------------------------------------------------------------------------------

   3 - filter("MGR" IS NULL)
   7 - access("EMP"."MGR"="MGR"."EMPNO")
       filter("EMP"."MGR" IS NOT NULL)</pre>
<p>Notice that there are actually two operations:</p>
<ul>
<li>UNION ALL (RECURSIVE WITH) BREADTH FIRST</li>
<li>UNION ALL (RECURSIVE WITH) DEPTH FIRST</li>
</ul>
<p>As their name suggest, the difference is due to the <a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e10592/statements_10002.htm#BCEDDGGE">search clause</a> that you can set to either BREADTH FIRST BY or DEPTH FIRST BY.</p>
<p>Reading an execution plan containing the “UNION ALL (RECURSIVE WITH)” operation is the same as reading one containing the “CONNECT BY WITH FILTERING” operation. As a matter of fact, the purpose of both operations is basically the same. Just notice that also the “PUMP” operation used in the execution plan differs. While in the former it is called “RECURSIVE WITH PUMP”, in the latter it is called “CONNECT BY PUMP”. But the difference, for the purpose of reading the execution plan, does not matter.</p>
<p>You find a full description on how to read such an execution plan in <a href="/2008/06/operation-connect-by-with-filtering/">this post</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.antognini.ch/2010/06/related-combine-operation-union-all-recursive-with/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

