<?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 Performance &#187; SQL Trace</title>
	<atom:link href="http://antognini.ch/category/oracledatabase/sql-trace/feed/" rel="self" type="application/rss+xml" />
	<link>http://antognini.ch</link>
	<description></description>
	<lastBuildDate>Sat, 17 Dec 2011 23:42:45 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>SQL Trace and Oracle Portal</title>
		<link>http://antognini.ch/2011/11/sql-trace-and-oracle-portal/</link>
		<comments>http://antognini.ch/2011/11/sql-trace-and-oracle-portal/#comments</comments>
		<pubDate>Tue, 29 Nov 2011 10:32:34 +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[SQL Trace]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1637</guid>
		<description><![CDATA[Recently I was involved in a project where I had to trace the database calls of an application based on Oracle Portal 10.1.4. The basic requirements were the following:

Tracing takes place in the production environment
Tracing has to be enable for a single user only
Instrumentation code cannot be added to the application

Given that Oracle Portal uses [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I was involved in a project where I had to trace the database calls of an application based on Oracle Portal 10.1.4. The basic requirements were the following:</p>
<ul>
<li>Tracing takes place in the production environment</li>
<li>Tracing has to be enable for a single user only</li>
<li>Instrumentation code cannot be added to the application</li>
</ul>
<p>Given that Oracle Portal uses a pool of connections and that for each HTTP call it can use several database sessions, statically enable SQL trace for specific sessions was not an option.</p>
<p>Knowing nothing about Oracle Portal I started RTFM and, gladly, I discovered that there is a simple way to inject a piece of code before and after a requested procedure is called. This is done by setting, via the administration GUI, the parameters <a href="http://docs.oracle.com/cd/B14099_19/web.1012/b14008/confmods.htm#sthref632">PlsqlBeforeProcedure</a> and <a href="http://docs.oracle.com/cd/B14099_19/web.1012/b14008/confmods.htm#sthref625">PlsqlAfterProcedure</a>. </p>
<p>Since Oracle Portal provides a function (<a href="http://docs.oracle.com/cd/B14099_19/portal.1012/b14134/pdg_pdk_plsql.htm#sthref1442">WWCTX_API.GET_USER</a>) to get the current user, I decided to create the following procedures to set/clear the client identifier before/after every call. Note that I added the call to SUBSTR and the exception handler to make sure that the procedures do not raise exceptions (hey, it’s a production system and I do not want to impact everyone!).</p>
<p><pre>CREATE PROCEDURE tvd_set_client_identifier AS
BEGIN
  dbms_session.set_identifier(substr(portal.wwctx_api.get_user,1,64));
EXCEPTION
  WHEN others THEN NULL;
END;</pre></p>
<p><pre>CREATE PROCEDURE tvd_clear_client_identifier AS
BEGIN
  dbms_session.clear_identifier;
EXCEPTION
  WHEN others THEN NULL;
END;</pre></p>
<p>To &#8220;enable&#8221; these procedures we did the following:</p>
<ul>
<li>Set PlsqlBeforeProcedure=portal.tvd_set_client_identifier</li>
<li>Set PlsqlAfterProcedure=portal.tvd_clear_client_identifier</li>
<li>Restarted the application server</li>
</ul>
<p>With this configuration in place enabling SQL trace for a single user was easily done by calling the <a href="http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_monitor.htm#i1002256">DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE</a> procedure by specifying the user to be traced as value for the CLIENT_ID parameter.</p>
]]></content:encoded>
			<wfw:commentRss>http://antognini.ch/2011/11/sql-trace-and-oracle-portal/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>TKPROF New Features in 11gR2 &#8211; 11.2.0.2</title>
		<link>http://antognini.ch/2010/10/tkprof-new-features-in-11gr2-11-2-0-2/</link>
		<comments>http://antognini.ch/2010/10/tkprof-new-features-in-11gr2-11-2-0-2/#comments</comments>
		<pubDate>Tue, 12 Oct 2010 23:42:01 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[11gR2]]></category>
		<category><![CDATA[SQL Trace]]></category>
		<category><![CDATA[TKPROF]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1349</guid>
		<description><![CDATA[The release 11.2.0.2 not only provides a lot of documented new features, but also provides undocumented ones. Today, I would like to spend few words about the undocumented changes introduced in the TKPROF output. In this area it is interesting to point out that after the introduction of wait events in Oracle9i, there were really [...]]]></description>
			<content:encoded><![CDATA[<p>The release 11.2.0.2 not only provides a lot of <a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e17128/chapter1_2.htm#NEWFTCH1-2">documented new features</a>, but also provides undocumented ones. Today, I would like to spend few words about the undocumented changes introduced in the TKPROF output. In this area it is interesting to point out that after the introduction of wait events in Oracle9i, there were really few enhancement in the formatting of the output.</p>
<p>To show you what’s new in the output, I executed the same commands I already used in the post covering the <a href="/2010/10/tkprof-new-features-in-11gr2-release-11-2-0-1/">11.2.0.1 new features of TKPROF</a>. I just removed the histogram on SH.SALES.CHANNEL_ID before running them (I did that because I was not interested in having different execution plans).</p>
<p>The relevant part of the output generated by TKPROF is the following:</p>
<ul>
<li>Version 11.2.0.1</li>
</ul>
<p><pre>SQL ID: 94mzsr37n3vz0
Plan Hash: 1550251865
SELECT *
FROM
 sh.sales WHERE channel_id = :channel_id

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        5      0.00       0.00          0          0          0           0
Execute      5      0.00       0.00          0          0          0           0
Fetch    61263      3.58       3.65        756      69331          2      918843
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    61273      3.59       3.65        756      69331          2      918843

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 34

Rows     Row Source Operation
-------  ---------------------------------------------------
 258025  PARTITION RANGE ALL PARTITION: 1 28 (cr=18811 pr=756 pw=0 time=664296 us cost=536 size=6661619 card=229711)
 258025   TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=18811 pr=756 pw=0 time=465129 us cost=536 size=6661619 card=229711)</pre></p>
<ul>
<li>Version 11.2.0.2</li>
</ul>
<p><pre>SQL ID: 94mzsr37n3vz0 Plan Hash: 1550251865

SELECT *
FROM
 sh.sales WHERE channel_id = :channel_id

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        5      0.00       0.00          0          0          0           0
Execute      5      0.00       0.00          0          0          0           0
Fetch    61263      3.58       3.65        756      69331          2      918843
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    61273      3.59       3.65        756      69331          2      918843

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 34
Number of plan statistics captured: 5

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
    258025     183769     540328  PARTITION RANGE ALL PARTITION: 1 28 (cr=13866 pr=151 pw=0 time=492737 us cost=536 size=6661619 card=229711)
    258025     183769     540328   TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=13866 pr=151 pw=0 time=396774 us cost=536 size=6661619 card=229711)</pre></p>
<p>As you can see the differences (in order or appearance, not relevance) are the following :</p>
<ul>
<li>The position of the “Plan Hash” information has slightly changed. By the way, that part of the output is different in every one of the last 4 releases! (11.1.0.6, 11.1.0.7, 11.2.0.1 and 11.2.0.2). It goes without saying that this difference is almost irrelevant.</li>
<li>Just after the parsing information a new line (“Number of plan statistics captured”) informs about the number of execution plans found in the trace file. Note that the presence of several execution plans also depends on the 11g feature I described <a href="/2009/02/11g-new-feature-in-dbms_monitor/">here</a>.</li>
<li>While 11.2.0.1 provides a single column (“Rows”) for reporting the number of rows returned by every row source operation, 11.2.0.2 provides three columns (“Rows (1st)”, “Rows (avg)” and “Rows (max)”). The idea is to point out whether the executions returned the same amount of data. For that purpose the output provides the number of rows returned by the first execution and, for all executions, the average and maximum number of returned rows.</li>
<li>The runtime statistics provided for each row source operation are also different. Specifically, while in 11.2.0.1 the values are the ones of the first execution found in the trace file, in 11.2.0.2 they are averages based on all executions.</li>
</ul>
<p>The new/changed information is good. But, be careful, averages hide a lot of information. So, while the new output is more useful than the old one, it’s not perfect. In fact, if you really want to know what happened at runtime, you have to give a look to the raw trace file information. In this case the information associated to the execution plans are the following (notice how the number or returned rows, attribute &#8220;cnt&#8221;, and the number of logical reads in consistent mode, attribute &#8220;cr&#8221;, changes between executions):</p>
<p><pre>STAT #182927356440 id=1 cnt=258025 pid=0 pos=1 obj=0 op=&#039;PARTITION RANGE ALL PARTITION: 1 28 (cr=18811 pr=756 pw=0 time=664296 us cost=536 size=6661619 card=229711)&#039;
STAT #182927356440 id=2 cnt=258025 pid=1 pos=1 obj=13821 op=&#039;TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=18811 pr=756 pw=0 time=465129 us cost=536 size=6661619 card=229711)&#039;
STAT #182927356440 id=1 cnt=540328 pid=0 pos=1 obj=0 op=&#039;PARTITION RANGE ALL PARTITION: 1 28 (cr=37596 pr=0 pw=0 time=1146677 us cost=536 size=6661619 card=229711)&#039;
STAT #182927356440 id=2 cnt=540328 pid=1 pos=1 obj=13821 op=&#039;TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=37596 pr=0 pw=0 time=739039 us cost=536 size=6661619 card=229711)&#039;
STAT #182927356440 id=1 cnt=118416 pid=0 pos=1 obj=0 op=&#039;PARTITION RANGE ALL PARTITION: 1 28 (cr=9515 pr=0 pw=0 time=421391 us cost=536 size=6661619 card=229711)&#039;
STAT #182927356440 id=2 cnt=118416 pid=1 pos=1 obj=13821 op=&#039;TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=9515 pr=0 pw=0 time=333077 us cost=536 size=6661619 card=229711)&#039;
STAT #182927356440 id=1 cnt=0 pid=0 pos=1 obj=0 op=&#039;PARTITION RANGE ALL PARTITION: 1 28 (cr=1635 pr=0 pw=0 time=218050 us cost=536 size=6661619 card=229711)&#039;
STAT #182927356440 id=2 cnt=0 pid=1 pos=1 obj=13821 op=&#039;TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=1635 pr=0 pw=0 time=218013 us cost=536 size=6661619 card=229711)&#039;
STAT #182927356440 id=1 cnt=2074 pid=0 pos=1 obj=0 op=&#039;PARTITION RANGE ALL PARTITION: 1 28 (cr=1774 pr=0 pw=0 time=13271 us cost=536 size=6661619 card=229711)&#039;
STAT #182927356440 id=2 cnt=2074 pid=1 pos=1 obj=13821 op=&#039;TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=1774 pr=0 pw=0 time=228613 us cost=536 size=6661619 card=229711)&#039;</pre></p>
<p>Another difference is related to the processing of trace files while aggregation is disabled (i.e. “aggregate=no”) and the SORT parameter is specified. The following two outputs, based on the same trace file as above, illustrate this (notice how several execution plans are displayed in the 11.2.0.1 output). Honestly, this could be seen as a bug fix.</p>
<ul>
<li>Version 11.2.0.1</li>
</ul>
<p><pre>SQL ID: 94mzsr37n3vz0
Plan Hash: 1550251865
SELECT *
FROM
 sh.sales WHERE channel_id = :channel_id

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        3      0.00       0.00          0          0          0           0
Execute      3      0.00       0.00          0          0          0           0
Fetch     8037      1.01       1.02          0      12924          0      120490
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     8043      1.01       1.02          0      12924          0      120490

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 34

Rows     Row Source Operation
-------  ---------------------------------------------------
 118416  PARTITION RANGE ALL PARTITION: 1 28 (cr=9515 pr=0 pw=0 time=421391 us cost=536 size=6661619 card=229711)
 118416   TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=9515 pr=0 pw=0 time=333077 us cost=536 size=6661619 card=229711)
      0  PARTITION RANGE ALL PARTITION: 1 28 (cr=1635 pr=0 pw=0 time=218050 us cost=536 size=6661619 card=229711)
      0   TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=1635 pr=0 pw=0 time=218013 us cost=536 size=6661619 card=229711)
   2074  PARTITION RANGE ALL PARTITION: 1 28 (cr=1774 pr=0 pw=0 time=13271 us cost=536 size=6661619 card=229711)
   2074   TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=1774 pr=0 pw=0 time=228613 us cost=536 size=6661619 card=229711)</pre></p>
<ul>
<li>Version 11.2.0.2</li>
</ul>
<p><pre>SQL ID: 94mzsr37n3vz0 Plan Hash: 1550251865

SELECT *
FROM
 sh.sales WHERE channel_id = :channel_id

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        3      0.00       0.00          0          0          0           0
Execute      3      0.00       0.00          0          0          0           0
Fetch     8037      1.01       1.02          0      12924          0      120490
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     8043      1.01       1.02          0      12924          0      120490

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 34
Number of plan statistics captured: 3

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
    118416      40163     118416  PARTITION RANGE ALL PARTITION: 1 28 (cr=4308 pr=0 pw=0 time=217571 us cost=536 size=6661619 card=229711)
    118416      40163     118416   TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=4308 pr=0 pw=0 time=259901 us cost=536 size=6661619 card=229711)</pre></p>
<p>The last thing I would like to point out, but without discussing the details, is that there are some changes in the trace files as well. The most obvious is the numbering of cursors…</p>
]]></content:encoded>
			<wfw:commentRss>http://antognini.ch/2010/10/tkprof-new-features-in-11gr2-11-2-0-2/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>TKPROF New Features in 11gR2 &#8211; Release 11.2.0.1</title>
		<link>http://antognini.ch/2010/10/tkprof-new-features-in-11gr2-release-11-2-0-1/</link>
		<comments>http://antognini.ch/2010/10/tkprof-new-features-in-11gr2-release-11-2-0-1/#comments</comments>
		<pubDate>Tue, 12 Oct 2010 16:45:51 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[SQL Trace]]></category>
		<category><![CDATA[TKPROF]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1375</guid>
		<description><![CDATA[While writing a post about the TKPROF new features in 11.2.0.2 I noticed that I didn’t write one about an important change introduced in 11.2.0.1. So, before finishing the other one, let’s have a look to what changed in 11.2.0.1.
One problem with TKPROF up to 11gR1 is that when the AGGREGATE parameter is set to [...]]]></description>
			<content:encoded><![CDATA[<p>While writing a post about the TKPROF new features in 11.2.0.2 I noticed that I didn’t write one about an important change introduced in 11.2.0.1. So, before finishing the other one, let’s have a look to what changed in 11.2.0.1.</p>
<p>One problem with TKPROF up to 11gR1 is that when the AGGREGATE parameter is set to YES (which is the default), all information belonging to cursors having the same text is aggregated in a single SQL statement. This is fine when all cursors were executed with the same execution plan. However, this is also done when a cursor was executed with several execution plans. As a result, only one execution plan is visible in the output. The others are lost.</p>
<p>As of 11gR2 this problem is solved. In fact, every SQL statement in the output is only related to a single execution plan.</p>
<p>To illustrate this enhancement let’s have a look to the TKPROF output for the trace file generated by the following commands (the test table is the one of the SH sample schema):</p>
<p><pre>VARIABLE channel_id NUMBER
EXECUTE dbms_monitor.session_trace_enable(plan_stat=&gt;&#039;all_executions&#039;, waits=&gt;false)
EXECUTE :channel_id := 2;
SELECT * FROM sh.sales WHERE channel_id = :channel_id;
EXECUTE :channel_id := 3;
SELECT * FROM sh.sales WHERE channel_id = :channel_id;
EXECUTE :channel_id := 4;
SELECT * FROM sh.sales WHERE channel_id = :channel_id;
EXECUTE :channel_id := 5;
SELECT * FROM sh.sales WHERE channel_id = :channel_id;
EXECUTE :channel_id := 9;
SELECT * FROM sh.sales WHERE channel_id = :channel_id;
EXECUTE dbms_monitor.session_trace_disable
SELECT value FROM v$diag_info WHERE name = &#039;Default Trace File&#039;;</pre></p>
<p>The essential thing to know about the query used in this test is that the selectivity strongly depends on the value of the CHANNEL_ID variable. The following query shows the actual distribution:</p>
<p><pre>SQL&gt; SELECT channel_id, count(*)
  2  FROM sh.sales
  3  GROUP BY channel_id
  4  ORDER BY channel_id;

CHANNEL_ID   COUNT(*)
---------- ----------
         2     258025
         3     540328
         4     118416
         9       2074</pre></p>
<p>Hence, when running the commands shown above, two execution plans are expected.</p>
<ul>
<li>When the selectivity is weak (high), the execution plan should be based on a full table scan.</li>
<li>When the selectivity is strong (low), the execution plan should be based on an index range scan.</li>
</ul>
<p>After generating the trace file with 11gR2, let’s have a look to the output provided by TKPROF.</p>
<ul>
<li>11.1.0.7</li>
</ul>
<p><pre>SELECT *
FROM
 sh.sales WHERE channel_id = :channel_id

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        5      0.04       0.03          0          0          0           0
Execute      5      0.00       0.00          0          0          0           0
Fetch    61263      2.95       2.99          0      66150          0      918843
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    61273      3.01       3.03          0      66150          0      918843

Misses in library cache during parse: 1
Misses in library cache during execute: 3
Optimizer mode: ALL_ROWS
Parsing user id: 34

Rows     Row Source Operation
-------  ---------------------------------------------------
 540328  PARTITION RANGE ALL PARTITION: 1 28 (cr=37596 pr=0 pw=0 time=1127079 us cost=531 size=7495485 card=258465)
 540328   TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=37596 pr=0 pw=0 time=705028 us cost=531 size=7495485 card=258465)</pre></p>
<ul>
<li>11.2.0.1</li>
</ul>
<p><pre>SQL ID: 94mzsr37n3vz0
Plan Hash: 1550251865
SELECT *
FROM
 sh.sales WHERE channel_id = :channel_id

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        3      0.04       0.03          0          0          0           0
Execute      3      0.00       0.00          0          0          0           0
Fetch    61122      2.94       2.98          0      65918          0      916769
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    61128      2.99       3.02          0      65918          0      916769

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 34

Rows     Row Source Operation
-------  ---------------------------------------------------
 540328  PARTITION RANGE ALL PARTITION: 1 28 (cr=37596 pr=0 pw=0 time=1127079 us cost=531 size=7495485 card=258465)
 540328   TABLE ACCESS FULL SALES PARTITION: 1 28 (cr=37596 pr=0 pw=0 time=705028 us cost=531 size=7495485 card=258465)</pre></p>
<p><pre>SQL ID: 94mzsr37n3vz0
Plan Hash: 3721375305
SELECT *
FROM
 sh.sales WHERE channel_id = :channel_id

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        2      0.00       0.00          0          0          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch      141      0.00       0.00          0        232          0        2074
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      145      0.01       0.01          0        232          0        2074

Misses in library cache during parse: 0
Misses in library cache during execute: 2
Optimizer mode: ALL_ROWS
Parsing user id: 34

Rows     Row Source Operation
-------  ---------------------------------------------------
   2074  PARTITION RANGE ALL PARTITION: 1 28 (cr=200 pr=0 pw=0 time=4602 us cost=325 size=78184 card=2696)
   2074   TABLE ACCESS BY LOCAL INDEX ROWID SALES PARTITION: 1 28 (cr=200 pr=0 pw=0 time=3979 us cost=325 size=78184 card=2696)
   2074    BITMAP CONVERSION TO ROWIDS (cr=32 pr=0 pw=0 time=993 us)
      3     BITMAP INDEX SINGLE VALUE SALES_CHANNEL_BIX PARTITION: 1 28 (cr=32 pr=0 pw=0 time=189 us)(object id 13980)</pre></p>
<p>As you can see, in the 11.1.0.7 output the 5 executions are associated to a single SQL statement. So, according to it, it is sensible to say that all 5 executions used the same execution plan based on a full table scan. However, as described above, this is not true. The 11.2.0.1 output doesn’t exhibit the same problem. In fact, the information is separated in two SQL statements. For that reason the section providing the information about a SQL statement begins with the information not only about the SQL ID, but also about the hash value of the execution plan. Note that the SQL ID is missing from the 11.1.0.7 output. No idea why… In fact, in 11.1.0.6, it is present. The hash value, however, was added in 11gR2.</p>
]]></content:encoded>
			<wfw:commentRss>http://antognini.ch/2010/10/tkprof-new-features-in-11gr2-release-11-2-0-1/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Evolution of a SQL Plan Baseline Based on a DELETE Statement</title>
		<link>http://antognini.ch/2010/06/evolution-of-a-sql-plan-baseline-based-on-a-delete-statement/</link>
		<comments>http://antognini.ch/2010/06/evolution-of-a-sql-plan-baseline-based-on-a-delete-statement/#comments</comments>
		<pubDate>Mon, 07 Jun 2010 14:21:26 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[SQL Trace]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1139</guid>
		<description><![CDATA[During an evolution the database engine compares the performance of two execution plans. The aim is to find out which one provides the better performance. For that purpose it has to run the SQL statement on which the SQL plan baseline is based and compare some execution statistics. The following output of the DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE function [...]]]></description>
			<content:encoded><![CDATA[<p>During an evolution the database engine compares the performance of two execution plans. The aim is to find out which one provides the better performance. For that purpose it has to run the SQL statement on which the SQL plan baseline is based and compare some execution statistics. The following output of the DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE function shows which statistics are compared.</p>
<p><pre>Plan was verified: Time used .06 seconds.
Plan passed performance criterion: 360.12 times better than baseline plan.
Plan was changed to an accepted plan.

                          Baseline Plan      Test Plan       Stats Ratio
                          -------------      ---------       -----------
Execution Status:              COMPLETE       COMPLETE
Rows Processed:                     100            100
Elapsed Time(ms):                 2.173           .033             65.85
CPU Time(ms):                     2.444              0
Buffer Gets:                        720              2               360
Physical Read Requests:               0              0
Physical Write Requests:              0              0
Physical Read Bytes:                  0              0
Physical Write Bytes:                 0              0
Executions:                           1              1</pre></p>
<p>For queries a regular execution can be performed. But, what happens for INSERT/UPDATE/MERGE/DELETE statements? Do the SQL engine really execute them and modify data?</p>
<p>To answer these questions let’s have a look to an example based on a DELETE statement…</p>
<ul>
<li>Setup a table used for the test:</li>
</ul>
<p><pre>SQL&gt; CREATE TABLE t (id, n, pad, CONSTRAINT t_pk PRIMARY KEY (id))
  2  AS
  3  SELECT rownum, mod(rownum,100), rpad(&#039;*&#039;,500,&#039;*&#039;)
  4  FROM dual
  5  CONNECT BY level &lt;= 10000;

SQL&gt; execute dbms_stats.gather_table_stats(ownname =&gt; user, tabname =&gt; &#039;t&#039;, method_opt =&gt; &#039;for all columns size 254&#039;)</pre></p>
<ul>
<li>Create a SQL plan baseline:</li>
</ul>
<p><pre>SQL&gt; ALTER SESSION SET optimizer_capture_sql_plan_baselines = TRUE;

SQL&gt; DELETE t WHERE n = 42;

SQL&gt; ROLLBACK;

SQL&gt; DELETE t WHERE n = 42;

SQL&gt; ROLLBACK;

SQL&gt; ALTER SESSION SET optimizer_capture_sql_plan_baselines = FALSE;</pre></p>
<ul>
<li>Add a non-accepted execution plan to the SQL plan baseline:</li>
</ul>
<p><pre>SQL&gt; CREATE INDEX i ON t (n);

SQL&gt; DELETE t WHERE n = 42;

SQL&gt; ROLLBACK;

SQL&gt; DELETE t WHERE n = 42;

SQL&gt; ROLLBACK;</pre></p>
<ul>
<li>Display the content of the SQL plan baseline (notice that two execution plans are available):</li>
</ul>
<p><pre>SQL&gt; SELECT * FROM table(dbms_xplan.display_sql_plan_baseline(&#039;SYS_SQL_373d78bbba048c24&#039;, NULL, &#039;basic&#039;));

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

--------------------------------------------------------------------------------
SQL handle: SYS_SQL_373d78bbba048c24
SQL text: DELETE t WHERE n = 42
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
Plan name: SQL_PLAN_3fgbsrfx093143bad20a0         Plan id: 1001201824
Enabled: YES     Fixed: NO      Accepted: YES     Origin: AUTO-CAPTURE
--------------------------------------------------------------------------------

Plan hash value: 3335594643

-----------------------------------
| Id  | Operation          | Name |
-----------------------------------
|   0 | DELETE STATEMENT   |      |
|   1 |  DELETE            | T    |
|   2 |   TABLE ACCESS FULL| T    |
-----------------------------------

--------------------------------------------------------------------------------
Plan name: SQL_PLAN_3fgbsrfx093144198692b         Plan id: 1100507435
Enabled: YES     Fixed: NO      Accepted: NO      Origin: AUTO-CAPTURE
--------------------------------------------------------------------------------

Plan hash value: 1582806765

----------------------------------
| Id  | Operation         | Name |
----------------------------------
|   0 | DELETE STATEMENT  |      |
|   1 |  DELETE           | T    |
|   2 |   INDEX RANGE SCAN| I    |
----------------------------------</pre></p>
<ul>
<li>Trace the evolution to find out what happens (notice that I deleted the output of the function because it is the one it is shown at the top of this post):</li>
</ul>
<p><pre>SQL&gt; execute dbms_monitor.session_trace_enable(plan_stat=&gt;&#039;ALL_EXECUTIONS&#039;)

SQL&gt; SELECT dbms_spm.evolve_sql_plan_baseline(
  2           sql_handle =&gt; &#039;SYS_SQL_373d78bbba048c24&#039;,
  3           plan_name  =&gt; &#039;&#039;,
  4           time_limit =&gt; 10,
  5           verify     =&gt; &#039;yes&#039;,
  6           commit     =&gt; &#039;yes&#039;
  7         )
  8  FROM dual;

SQL&gt; execute dbms_monitor.session_trace_disable

SQL&gt; SELECT value
  2  FROM v$diag_info
  3  WHERE name = &#039;Default Trace File&#039;;

VALUE
-------------------------------------------------------------------
/u00/app/oracle/diag/rdbms/dba112/DBA112/trace/DBA112_ora_17200.trc</pre></p>
<p>Now that the trace file was generated, let&#8217;s have a look to its content. The relevant parts are two: the first one is related to the execution of the accepted execution plan, and the second one is related to the execution of the non-accepted one.</p>
<p><pre>PARSING IN CURSOR #11 len=45 dep=1 uid=90 oct=7 lid=90 tim=1275524159625080 hv=4077337184 ad=&#039;325c9f10&#039; sqlid=&#039;5fwyncmthffm0&#039;
/* SQL Analyze(25,0) */ DELETE t WHERE n = 42
END OF STMT
PARSE #11:c=1000,e=652,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,plh=1001201824,tim=1275524159625078
EXEC #11:c=4999,e=5670,p=0,cr=720,cu=0,mis=0,r=0,dep=1,og=1,plh=1001201824,tim=1275524159630752
EXEC #11:c=2000,e=1718,p=0,cr=720,cu=0,mis=0,r=0,dep=1,og=1,plh=1001201824,tim=1275524159632613
EXEC #11:c=2000,e=1511,p=0,cr=720,cu=0,mis=0,r=0,dep=1,og=1,plh=1001201824,tim=1275524159634156
EXEC #11:c=2000,e=1542,p=0,cr=720,cu=0,mis=0,r=0,dep=1,og=1,plh=1001201824,tim=1275524159636144
EXEC #11:c=2000,e=1552,p=0,cr=720,cu=0,mis=0,r=0,dep=1,og=1,plh=1001201824,tim=1275524159638151
EXEC #11:c=3998,e=4015,p=0,cr=720,cu=0,mis=0,r=0,dep=1,og=1,plh=1001201824,tim=1275524159642613
EXEC #11:c=3000,e=2905,p=0,cr=720,cu=0,mis=0,r=0,dep=1,og=1,plh=1001201824,tim=1275524159645549
EXEC #11:c=2000,e=1506,p=0,cr=720,cu=0,mis=0,r=0,dep=1,og=1,plh=1001201824,tim=1275524159647151
EXEC #11:c=2000,e=1562,p=0,cr=720,cu=0,mis=0,r=0,dep=1,og=1,plh=1001201824,tim=1275524159649160
EXEC #11:c=2999,e=2440,p=0,cr=720,cu=0,mis=0,r=0,dep=1,og=1,plh=1001201824,tim=1275524159652037
CLOSE #11:c=0,e=3,dep=1,type=0,tim=1275524159652065</pre></p>
<p><pre>PARSING IN CURSOR #5 len=45 dep=1 uid=90 oct=7 lid=90 tim=1275524159657503 hv=4077337184 ad=&#039;325c9f10&#039; sqlid=&#039;5fwyncmthffm0&#039;
/* SQL Analyze(25,0) */ DELETE t WHERE n = 42
END OF STMT
PARSE #5:c=1000,e=859,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,plh=1100507435,tim=1275524159657499
EXEC #5:c=0,e=52,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=1,plh=1100507435,tim=1275524159657625
EXEC #5:c=0,e=0,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=1,plh=1100507435,tim=1275524159657647
EXEC #5:c=0,e=31,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=1,plh=1100507435,tim=1275524159657972
EXEC #5:c=0,e=5,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=1,plh=1100507435,tim=1275524159658071
EXEC #5:c=0,e=0,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=1,plh=1100507435,tim=1275524159658071
EXEC #5:c=0,e=0,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=1,plh=1100507435,tim=1275524159658071
EXEC #5:c=0,e=0,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=1,plh=1100507435,tim=1275524159658071
EXEC #5:c=0,e=0,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=1,plh=1100507435,tim=1275524159658071
EXEC #5:c=0,e=0,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=1,plh=1100507435,tim=1275524159658071
EXEC #5:c=0,e=0,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=1,plh=1100507435,tim=1275524159658071
CLOSE #5:c=0,e=0,dep=1,type=0,tim=1275524159658071</pre></p>
<p>In the previous output notice that:</p>
<ul>
<li>The PLH attribute of the EXEC lines shows that two execution plans are used.</li>
<li>Each execution plan was executed 10 times (in practice the number varies according to the elapsed time; i.e. for longer executions a single run might be enough to determine whether an execution plan has to be accepted).</li>
<li>Even though I set the PLAN_STAT parameter to ALL_EXECUTIONS (if you don’t know what the PLAN_STAT parameter is for, have a look to <a href="/2009/02/11g-new-feature-in-dbms_monitor/">this post</a>) the STAT lines (the execution plan) are not available in the trace file.</li>
</ul>
<p>According to this information the SQL statement is executed. But, if you check the table after the evolution, the data is still there. And that, honestly, is not an option! In addition, no ROLLBACK is executed (no XCTEND lines are present in the trace file). So, it seems that the SQL statement is <em>not</em> executed.</p>
<p>What I really miss in the trace file are the execution plans associated to the executions to check what the different operations of the execution plan did. The only way I found to have them, it is to add the GATHER_PLAN_STATISTICS hint into the SQL statement itself (also setting the STATISTICS_LEVEL parameter and checking a view like V$SQL_PLAN_STATISTICS_ALL did not help). The content of the trace file, formatted by <a href="/category/apmtools/tvdxtat/">TVD$XTAT</a>, is the following:</p>
<p><pre>Optimizer Mode       ALL_ROWS
Hash Value           1001201824
Number of Executions 10

        Rows Operation
------------ ---------------------------------------------------------------------------------------
           0 DELETE  T (cr=720 pr=25 pw=0 time=0 us)
           0   TABLE ACCESS FULL T (cr=720 pr=25 pw=0 time=0 us cost=84 size=700 card=100)

Optimizer Mode       ALL_ROWS
Hash Value           1100507435
Number of Executions 10

        Rows Operation
------------ ---------------------------------------------------------------------------------------
           0 DELETE  T (cr=2 pr=0 pw=0 time=0 us)
           0   INDEX RANGE SCAN I (cr=2 pr=0 pw=0 time=0 us cost=1 size=700 card=100) (object id 93840)</pre></p>
<p>Notice that while the number of logical reads (CR attribute) matches the report generated by the evolution, the number of rows returned by both steps of the execution plans is 0. And that, even though the index range scan should return 100 rows.</p>
<p>In summary, during an evolution the SQL engine processes the SQL statements in a special way. The data is accessed, but not modified. Hence, SQL statements are only partially executed. I do not regard this fact as a problem, though. In fact, the operations that modify data should always perform the same work independently on how the data to be modified is located (in the example given here, either with a full table scan or an index range scan).</p>
]]></content:encoded>
			<wfw:commentRss>http://antognini.ch/2010/06/evolution-of-a-sql-plan-baseline-based-on-a-delete-statement/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Optimizer Mode Mismatch Does Not Prevent Sharing of Child Cursor!?!?</title>
		<link>http://antognini.ch/2010/06/optimizer-mode-mismatch-does-not-prevent-sharing-of-child-cursor/</link>
		<comments>http://antognini.ch/2010/06/optimizer-mode-mismatch-does-not-prevent-sharing-of-child-cursor/#comments</comments>
		<pubDate>Thu, 03 Jun 2010 17:40:03 +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[Bug]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[SQL Trace]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=1113</guid>
		<description><![CDATA[The aim of this post is to describe a strange (buggy) situation that I observed recently. But before doing that, I shortly summarize what a parent cursor and a child cursor are as well as when they can be shared. By the way, I borrowed this description from the pages 20/21 of my book. Hence, [...]]]></description>
			<content:encoded><![CDATA[<p>The aim of this post is to describe a strange (buggy) situation that I observed recently. But before doing that, I shortly summarize what a parent cursor and a child cursor are as well as when they can be shared. By the way, I borrowed this description from the pages 20/21 of my <a href="/top">book</a>. Hence, if you are interested in more information about this topic refer to it…</p>
<p>The result of a parse operation is a parent cursor and a child cursor stored in the library cache.</p>
<p>The key information related to a parent cursor is the text of the SQL statement. Therefore, several SQL statements share the same parent cursor if their text is exactly the same (note that there is at least an exception to this, specifically when cursor sharing is used). In the following example, four SQL statements are executed. Two have the same text. Two others differ only because of lowercase and uppercase letters or blanks. Through the V$SQLAREA view, it is possible to confirm that three distinct parent cursors were created.</p>
<p><pre>SQL&gt; ALTER SYSTEM FLUSH SHARED_POOL;

SQL&gt; SELECT * FROM t WHERE n = 1234;

SQL&gt; select * from t where n = 1234;

SQL&gt; SELECT * FROM t WHERE n=1234;

SQL&gt; SELECT * FROM t WHERE n = 1234;

SQL&gt; SELECT sql_id, sql_text, executions
  2  FROM v$sqlarea
  3  WHERE sql_text LIKE &#039;%1234&#039;;

SQL_ID        SQL_TEXT                          EXECUTIONS
------------- --------------------------------- ----------
2254m1487jg50 select * from t where n = 1234             1
g9y3jtp6ru4cb SELECT * FROM t WHERE n = 1234             2
7n8p5s2udfdsn SELECT * FROM t WHERE n=1234               1</pre></p>
<p>The key information related to a child cursor is the execution plan and the execution environment related to it. The execution environment is important because if it changes, the execution plan might change as well. As a result, several SQL statements are able to share the same child cursor only if they share the same parent cursor and their execution environments are compatible. To illustrate, the same SQL statement is executed with two different values of the initialization OPTIMIZER_MODE parameter. The result is that a single parent cursor and two child cursors are created.</p>
<p><pre>SQL&gt; ALTER SESSION SET optimizer_mode = all_rows;

SQL&gt; SELECT count(*) FROM t;

COUNT(*)
----------
      1000

SQL&gt; ALTER SESSION SET optimizer_mode = first_rows_10;

SQL&gt; SELECT count(*) FROM t;

COUNT(*)
----------
      1000

SQL&gt; SELECT sql_id, child_number, sql_text, optimizer_mode, plan_hash_value
  2  FROM v$sql
  3  WHERE sql_id = (SELECT prev_sql_id
  4  FROM v$session
  5  WHERE sid = sys_context(&#039;userenv&#039;,&#039;sid&#039;));

SQL_ID        CHILD_NUMBER SQL_TEXT               OPTIMIZER_MODE PLAN_HASH_VALUE
------------- ------------ ---------------------- -------------- ---------------
5tjqf7sx5dzmj            0 SELECT count(*) FROM t ALL_ROWS            2966233522
5tjqf7sx5dzmj            1 SELECT count(*) FROM t FIRST_ROWS          2966233522</pre></p>
<p>To know which mismatch led to several child cursors, you can query the V$SQL_SHARED_CURSOR view.</p>
<p><pre>SQL&gt; SELECT child_number, optimizer_mode_mismatch
  2  FROM v$sql_shared_cursor
  3  WHERE sql_id = &#039;5tjqf7sx5dzmj&#039;;

CHILD_NUMBER OPTIMIZER_MODE_MISMATCH
------------ -----------------------
           0 N
           1 Y</pre></p>
<p>So far, so good… Now, let’s see what’s strange…</p>
<p>The interesting thing to point out about the previous example is that while I set FIRST_ROWS_10 as optimizer mode, the V$SQL view displayed the value FIRST_ROWS. Mhmm… That’s strange… They are two different optimizer modes. They cannot be considered equivalent. What are the implications? It is just the view that provides the wrong information or the database engine is able to share the same child cursor even with two different values of the OPTIMIZER_MODE parameter? Let’s try it with FIRST_ROWS (i.e. without “_10”)…</p>
<p><pre> SQL&gt; ALTER SESSION SET optimizer_mode = first_rows;

SQL&gt; SELECT sql_id, child_number, sql_text, optimizer_mode, executions
  2  FROM v$sql
  3  WHERE sql_id = (SELECT prev_sql_id
  4                  FROM v$session
  5                  WHERE sid = sys_context(&#039;userenv&#039;,&#039;sid&#039;));

SQL_ID        CHILD_NUMBER SQL_TEXT                          OPTIMIZER_MODE EXECUTIONS
------------- ------------ --------------------------------- -------------- ----------
5tjqf7sx5dzmj            0 SELECT count(*) FROM t            ALL_ROWS                1
5tjqf7sx5dzmj            1 SELECT count(*) FROM t            FIRST_ROWS              2</pre></p>
<p>Oh, damn! Even though the OPTIMIZER MODE is set to a different value the same child cursor is used. Since in this particular situation the execution plans associated to both child cursors are the same (their hash value are equal), it’s not a real problem. But, in practice, it might be possible that two different optimizer modes lead to different execution plans. The following example illustrates this.</p>
<ul>
<li>Build a table for the test:</li>
</ul>
<p><pre>SQL&gt; CREATE TABLE t AS
  2  SELECT rownum AS id, rpad(&#039;*&#039;,500,&#039;*&#039;) AS pad
  3  FROM dual
  4  CONNECT BY level &lt;= 1000;

SQL&gt; CREATE UNIQUE INDEX i ON t (id);

SQL&gt; execute dbms_stats.gather_table_stats(user, &#039;T&#039;)</pre></p>
<ul>
<li>Show that different values of the OPTIMIZER_MODE parameter lead to different execution plans:</li>
</ul>
<p><pre>SQL&gt; ALTER SESSION SET optimizer_mode = FIRST_ROWS_1;

SQL&gt; EXPLAIN PLAN FOR SELECT * FROM t WHERE id &lt;= 500;

SQL&gt; SELECT * FROM table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
Plan hash value: 242607798

------------------------------------------------------------------------------------
| Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |     3 |  1515 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T    |     3 |  1515 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | I    |       |       |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------

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

   2 - access(&quot;ID&quot;&lt;=500)

SQL&gt; ALTER SESSION SET optimizer_mode = FIRST_ROWS_1000;

SQL&gt; EXPLAIN PLAN FOR SELECT * FROM t WHERE id &lt;= 500;

SQL&gt; SELECT * FROM table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   500 |   246K|    10   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |   500 |   246K|    10   (0)| 00:00:01 |
--------------------------------------------------------------------------

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

   1 - filter(&quot;ID&quot;&lt;=500)</pre></p>
<ul>
<li>Execute the test query with both values of the OPTIMIZER_MODE parameter:</li>
</ul>
<p><pre>SQL&gt; ALTER SYSTEM FLUSH SHARED_POOL;

SQL&gt; ALTER SESSION SET optimizer_mode = FIRST_ROWS_1;

SQL&gt; SELECT * FROM t WHERE id &lt;= 500;

        ID PAD
---------- ----------
         1 **********
         2 **********
…
       499 **********
       500 **********

SQL&gt; ALTER SESSION SET optimizer_mode = FIRST_ROWS_1000;

SQL&gt; SELECT * FROM t WHERE id &lt;= 500;

        ID PAD
---------- ----------
         1 **********
         2 **********
…
       499 **********
       500 **********</pre></p>
<ul>
<li>Show that a single execution plan was used for both executions:</li>
</ul>
<p><pre>SQL&gt; SELECT * FROM table(dbms_xplan.display_cursor(NULL,NULL));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
SQL_ID  2vw03p929jzgz, child number 0
-------------------------------------
SELECT * FROM t WHERE id &lt;= 500

Plan hash value: 242607798

------------------------------------------------------------------------------------
| Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| T    |     3 |  1515 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | I    |       |       |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------

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

   2 - access(&quot;ID&quot;&lt;=500)

SQL&gt; SELECT sql_id, child_number, executions, optimizer_mode
  2  FROM v$sql
  3  WHERE sql_id = &#039;2vw03p929jzgz&#039;;

SQL_ID        CHILD_NUMBER EXECUTIONS OPTIMIZER_MODE
------------- ------------ ---------- --------------
2vw03p929jzgz            0          2 FIRST_ROWS</pre></p>
<p>Even though it is not very likely that this bug (yes, in my opinion something like this cannot be considered a restriction of the implementation…) has an impact on a production system, I really don’t understand why the developers didn’t implement it correctly. It should not be that difficult to manage a byte containing the information about the used optimizer mode! Note that this is not the only case where something like that happens with the first rows optimizer mode. For example, also in a trace file generated through SQL trace no difference is made between the old and the new first row optimizer. So, it seams that they really got it wrong.</p>
]]></content:encoded>
			<wfw:commentRss>http://antognini.ch/2010/06/optimizer-mode-mismatch-does-not-prevent-sharing-of-child-cursor/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Analyzing a SQL Trace File with SQL Statements</title>
		<link>http://antognini.ch/2010/04/analyzing-a-sql-trace-file-with-sql-statements/</link>
		<comments>http://antognini.ch/2010/04/analyzing-a-sql-trace-file-with-sql-statements/#comments</comments>
		<pubDate>Tue, 27 Apr 2010 08:34:42 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[11gR1]]></category>
		<category><![CDATA[11gR2]]></category>
		<category><![CDATA[SQL Trace]]></category>
		<category><![CDATA[TKPROF]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=995</guid>
		<description><![CDATA[As of Oracle Database 11g the DBMS_SQLTUNE package provides the SELECT_SQL_TRACE function. Its purpose is to load the content of a SQL trace file into a SQL tuning set. But, as it often happens, a feature can be (mis)used for another purpose. The aim of this post is to show how to take advantage of [...]]]></description>
			<content:encoded><![CDATA[<p>As of Oracle Database 11g the DBMS_SQLTUNE package provides the SELECT_SQL_TRACE function. Its purpose is to load the content of a SQL trace file into a SQL tuning set. But, as it often happens, a feature can be (mis)used for another purpose. The aim of this post is to show how to take advantage of this function to display through SQL statements the content of a SQL trace file.</p>
<p>Note that the SELECT_SQL_TRACE function is not available in version 11.1.0.6. Refer to the MOS note 790806.1 for additional information. Hence, the code shown in this post works as of 11.1.0.7 only.</p>
<p>First of all, let’s setup the scene.</p>
<ul>
<li>Create the necessary database objects and gather object statistics:</li>
</ul>
<p><pre>SQL&gt; CREATE TABLE t
  2  AS
  3  SELECT rownum AS id, rpad(&#039;*&#039;,1000,&#039;*&#039;) AS pad
  4  FROM dual
  5  CONNECT BY level &lt;= 10000
  6  ORDER BY dbms_random.value;

SQL&gt; ALTER TABLE t ADD CONSTRAINT t_pk PRIMARY KEY (id);

SQL&gt; BEGIN
  2    dbms_stats.gather_table_stats(
  3      ownname          =&gt; user,
  4      tabname          =&gt; &#039;t&#039;,
  5      estimate_percent =&gt; 100,
  6      method_opt       =&gt; &#039;for all columns size 1&#039;
  7    );
  8  END;
  9  /</pre></p>
<ul>
<li>Enable SQL trace (if you are asking yourself why I specify the PLAN_STAT parameter, have a look to this <a href="/2009/02/11g-new-feature-in-dbms_monitor/">post</a>):</li>
</ul>
<p><pre>SQL&gt; execute dbms_monitor.session_trace_enable(binds =&gt; TRUE, plan_stat =&gt; &#039;ALL_EXECUTIONS&#039;)</pre></p>
<ul>
<li>Run some queries:</li>
</ul>
<p><pre>SQL&gt; EXECUTE :id := 10;

SQL&gt; SELECT count(pad) FROM t WHERE id &lt; :id;

COUNT(PAD)
----------
         9

SQL&gt; EXECUTE :id := 990;

SQL&gt; SELECT count(pad) FROM t WHERE id &lt; :id;

COUNT(PAD)
----------
       989

SQL&gt; SELECT count(pad) FROM t WHERE id &lt; :id;

COUNT(PAD)
----------
       989

SQL&gt; EXECUTE :id := 20;

SQL&gt; SELECT count(pad) FROM t WHERE id &lt; :id;

COUNT(PAD)
----------
        19

SQL&gt; SELECT sum(id) FROM t;

   SUM(ID)
----------
  50005000</pre></p>
<ul>
<li>Disable SQL trace and retrieve the name of the SQL trace file:</li>
</ul>
<p><pre>SQL&gt; execute dbms_monitor.session_trace_disable

SQL&gt; SELECT value
  2  FROM v$diag_info
  3  WHERE name = &#039;Default Trace File&#039;;

VALUE
-------------------------------------------------------------------
/u00/app/oracle/diag/rdbms/dba112/DBA112/trace/DBA112_ora_26790.trc</pre></p>
<ul>
<li>Create a directory to read the SQL trace file through SQL statements</li>
</ul>
<p><pre>SQL&gt; CREATE DIRECTORY trace AS &#039;/u00/app/oracle/diag/rdbms/dba112/DBA112/trace/&#039;;</pre></p>
<p>Now that we have a SQL trace file, let’s see how we can read its content with some simple queries and what kind of information we can extract.</p>
<ul>
<li>Retrieve a list of SQL statements executed by the current user (to exclude the recursive SQL statements executed by SYS) including their elapsed time and number of executions:</li>
</ul>
<p><pre>SQL&gt; SELECT sql_id,
  2         sum(elapsed_time) AS elapsed_time,
  3         sum(executions) AS executions,
  4         round(sum(elapsed_time)/sum(executions)) AS elapsed_time_per_execution
  5  FROM table(dbms_sqltune.select_sql_trace(
  6               directory =&gt; &#039;TRACE&#039;,
  7               file_name =&gt; &#039;DBA112_ora_26790.trc&#039;,
  8               select_mode =&gt; 2 -- all executions
  9            )) t
 10  WHERE parsing_schema_name = user
 11  GROUP BY sql_id
 12  ORDER BY elapsed_time DESC;

SQL_ID        ELAPSED_TIME EXECUTIONS ELAPSED_TIME_PER_EXECUTION
------------- ------------ ---------- --------------------------
asth1mx10aygn       249757          4                      62439
6tgnxwpymddqc         4200          1                       4200</pre></p>
<ul>
<li>Retrieve the text of a particular SQL statement:</li>
</ul>
<p><pre>SQL&gt; SELECT sql_text
  2  FROM table(dbms_sqltune.select_sql_trace(
  3               directory =&gt; &#039;TRACE&#039;,
  4               file_name =&gt; &#039;DBA112_ora_26790.trc&#039;,
  5               select_mode =&gt; 1 -- only first execution
  6            )) t
  7  WHERE sql_id = &#039;asth1mx10aygn&#039;;

SQL_TEXT
---------------------------------------
SELECT count(pad) FROM t WHERE id &lt; :id</pre></p>
<ul>
<li>Retrieve more execution statistics about a particular SQL statement:</li>
</ul>
<p><pre>SQL&gt; SELECT plan_hash_value, executions, fetches, elapsed_time, cpu_time, disk_reads, buffer_gets, rows_processed
  2  FROM table(dbms_sqltune.select_sql_trace(
  3               directory =&gt; &#039;TRACE&#039;,
  4               file_name =&gt; &#039;DBA112_ora_26790.trc&#039;,
  5               select_mode =&gt; 2 -- all executions
  6            )) t
  7  WHERE sql_id = &#039;asth1mx10aygn&#039;
  8  ORDER BY elapsed_time DESC;

PLAN_HASH_VALUE EXECUTIONS    FETCHES ELAPSED_TIME   CPU_TIME DISK_READS BUFFER_GETS ROWS_PROCESSED
--------------- ---------- ---------- ------------ ---------- ---------- ----------- --------------
     4294967295          1          2       129056     127981        731         992              1
     4294967295          1          2       113667     112982        691        1434              1
     4294967295          1          2         5993       6999         11          11              1
     4294967295          1          2         1041       1000          0          21              1</pre></p>
<ul>
<li>Retrieve the value of the bind variables used to execute a particular SQL statement:</li>
</ul>
<p><pre>SQL&gt; SELECT elapsed_time,
  2         value(b).gettypename() AS type,
  3         value(b).accessnumber() AS value
  4  FROM table(dbms_sqltune.select_sql_trace(
  5               directory =&gt; &#039;TRACE&#039;,
  6               file_name =&gt; &#039;DBA112_ora_26790.trc&#039;,
  7               select_mode =&gt; 2 -- all executions
  8            )) t,
  9       table(bind_list) b
 10  WHERE sql_id = &#039;asth1mx10aygn&#039;
 11  ORDER BY elapsed_time DESC;

ELAPSED_TIME TYPE       VALUE
------------ ---------- -----
      129056 SYS.NUMBER   990
      113667 SYS.NUMBER   990
        5993 SYS.NUMBER    10
        1041 SYS.NUMBER    20</pre></p>
<p>Even though everything seems fine, there is an error in the output of one of the queries. Specifically, the hash value of the execution plan is not always the right one (the same value is used for all executions). In fact, because of extended cursor sharing (a.k.a. adaptive cursor sharing) several execution plans were used. This can be confirmed by analyzing the trace file with TKPROF (or another analyzer like <a href="/category/apmtools/tvdxtat/">TVD$XTAT</a>). In this case the TKPROF output, generated with the AGGREGATE option set to &#8220;NO&#8221; provides the following information (notice that the hash value of the third execution is different):</p>
<p><pre>...
SQL ID: asth1mx10aygn
Plan Hash: 4270555908
SELECT count(pad) FROM t WHERE id &lt; :id
...
SQL ID: asth1mx10aygn
Plan Hash: 4270555908
SELECT count(pad) FROM t WHERE id &lt; :id
...
SQL ID: asth1mx10aygn
Plan Hash: 2966233522
SELECT count(pad) FROM t WHERE id &lt; :id
...
SQL ID: asth1mx10aygn
Plan Hash: 4270555908
SELECT count(pad) FROM t WHERE id &lt; :id
...</pre></p>
<p>The other information that can be retrieved through the SELECT_SQL_TRACE function is the execution plan. Unfortunately, extracting it directly through the function is inconvenient. The reason is that you should do the formatting yourself. Much easier is to load the information into a SQL tuning set and, then, to use the DBMS_XPLAN package to show its content. The following example illustrates this:</p>
<p><pre>SQL&gt; DECLARE
  2    c sys_refcursor;
  3  BEGIN
  4    dbms_sqltune.create_sqlset(&#039;TEST&#039;);
  5    OPEN c FOR
  6      SELECT value(t)
  7      FROM table(dbms_sqltune.select_sql_trace(
  8               directory =&gt; &#039;TRACE&#039;,
  9               file_name =&gt; &#039;DBA112_ora_26790.trc&#039;,
 10               select_mode =&gt; 2 -- all executions
 11                )) t;
 12    dbms_sqltune.load_sqlset(&#039;TEST&#039;, c);
 13    CLOSE c;
 14  END;
 15  /

SQL&gt; SELECT *
  2  FROM table(dbms_xplan.display_sqlset(
  3               sqlset_name =&gt; &#039;TEST&#039;,
  4               sql_id =&gt; &#039;asth1mx10aygn&#039;
  5            ));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------
SQL Tuning Set Name: TEST
SQL Tuning Set Owner: CHA
SQL_ID: asth1mx10aygn
SQL Text: SELECT count(pad) FROM t WHERE id &lt; :id
-----------------------------------------------------------------------------

-----------------------------------------------------------------------------
| Id  | Operation                    | Name                 | Bytes | Cost  |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                      |       |       |
|   1 |  SORT AGGREGATE              |                      |       |       |
|   2 |   TABLE ACCESS BY INDEX ROWID| UNKNOWN_OBJECT_90222 |  9045 |    11 |
|   3 |    INDEX RANGE SCAN          | UNKNOWN_OBJECT_90223 |       |     2 |
-----------------------------------------------------------------------------</pre></p>
<p>Notice that also in this case only one execution plan is shown.</p>
<p>All in all, this is an interesting feature. For sure it does not replace a profiler (mainly because the wait events are not shown), but it might be useful in some situations.</p>
]]></content:encoded>
			<wfw:commentRss>http://antognini.ch/2010/04/analyzing-a-sql-trace-file-with-sql-statements/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Inserts Experiencing an Increasing CPU Consumption</title>
		<link>http://antognini.ch/2010/03/inserts-experiencing-an-increasing-cpu-consumption/</link>
		<comments>http://antognini.ch/2010/03/inserts-experiencing-an-increasing-cpu-consumption/#comments</comments>
		<pubDate>Mon, 08 Mar 2010 21:23:42 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[10gR2]]></category>
		<category><![CDATA[SQL Trace]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=941</guid>
		<description><![CDATA[Last week I had to analyze a strange performance problem. Since the cause/solution was somehow surprising, at least for me, I thought to share it with you.
Let me start by quickly describing the setup and what was done to reproduce the problem:

Database version: Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 (64-bit)
Operating system: Solaris 10 (SPARC)
To [...]]]></description>
			<content:encoded><![CDATA[<p>Last week I had to analyze a strange performance problem. Since the cause/solution was somehow surprising, at least for me, I thought to share it with you.</p>
<p>Let me start by quickly describing the setup and what was done to reproduce the problem:</p>
<ul>
<li>Database version: Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 (64-bit)</li>
<li>Operating system: Solaris 10 (SPARC)</li>
<li>To simulate a load job, a simple SQL*Plus script that executes a COPY command is used. Its purpose is to load about 100,000 rows in a table. Let’s call this table T1.</li>
<li>All modifications in T1 have to be logged into another table. Let’s call it T2. For this purpose, on T1 there are triggers that insert one row into T2 for each inserted, deleted and updated row.</li>
</ul>
<p>The strange thing was that the rate of the inserts performed by the script decreased over time. In fact, while at the beginning of the processing about 500 rows per second were inserted into T1 (and, therefore, T2), at the end of the processing only about 50 rows per second were processed.</p>
<p>The first thing I did to find out what the problem was is to trace one run by enabling SQL trace. This analysis pointed out that two SQL statements (the ones inserting data into T1 and T2) were responsible for most of the elapsed time. This is not a surprise, of course. The interesting thing was that most of the time was spent on CPU.</p>
<p>Since the rate of the inserts decreased over time, I extracted from the trace file all the lines providing information about the executions of the INSERT statement on T1 and loaded that data into Excel. Then, I created one chart for each performance figure. From all of them the following, that shows the amount of CPU used for every single execution, was the most interesting. In fact, it shows that while at the beginning of the processing one insert uses about 30 milliseconds of CPU, at the end it uses about 300 milliseconds of CPU for doing the same work. Note that all other charts did not show such a behavior. For example, the number of PIO and LIO were exactly the same at the beginning and at the end of the processing.</p>
<p><a href="/images/chart1_20100308.jpg" rel="thumbnail"><br />
<img src="/images/chart1_20100308.jpg" alt="Chart 1 - With trigger on T2"  width="640" style="float:none; border:none" /><br />
</a></p>
<p>Since the trace file was not able to provide further information to investigate the problem, I started looking at V$SESSTAT. The aim was to find another statistic experiencing a similar increase. The search pointed out that the statistic “session uga memory” was also increasing during the processing. In fact, while at the beginning of the processing the session was using about 5MB of UGA, at the end of the processing about 110MB were used. This is strange and, as far as I know, there is no good reason for such a behavior. Hence, it was time to review the code of the triggers. While doing so I noticed, by chance, that a trigger was also available on T2 (the table used to store the log about all modifications). The strange thing was its definition:</p>
<p><pre>CREATE OR REPLACE TRIGGER t2 AFTER INSERT ON t2 FOR EACH ROW
BEGIN
  /* execute the referential-integrity actions */
  DECLARE
    NUMROWS INTEGER;
  BEGIN
    numrows:=1;
  END;
END;</pre></p>
<p>As you can see the trigger does nothing. Apparently, it exists just because triggers are used to implement integrity constraints (something you should avoid, by the way…) and, as a result, they were automatically created for each table. And, in case of T2, there is no constraint to check.</p>
<p>Since the trigger is pointless, I disabled it. After that, surprisingly, it was no longer possible to reproduce the problem! The following chart, created in the same way as the previous one, shows that without the trigger on T2 the CPU utilization is constant during the whole processing.</p>
<p><a href="/images/chart1_20100308.jpg" rel="thumbnail"><br />
<img src="/images/chart2_20100308.jpg" alt="Chart 2 - Without trigger on T2" width="640" rel="thumbnail" style="float:none; border:none"/><br />
</a></p>
<p>Therefore, for some unknown reasons, the pointless trigger was the cause of the problem.</p>
<p>By the way, once the trigger was disabled also the UGA memory was no longer increasing. Hence, to me it seems that the customer hit a bug…</p>
]]></content:encoded>
			<wfw:commentRss>http://antognini.ch/2010/03/inserts-experiencing-an-increasing-cpu-consumption/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Synthetic Commits and Rollbacks</title>
		<link>http://antognini.ch/2009/08/synthetic-commits-and-rollbacks/</link>
		<comments>http://antognini.ch/2009/08/synthetic-commits-and-rollbacks/#comments</comments>
		<pubDate>Wed, 19 Aug 2009 21:50:51 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[SQL Trace]]></category>
		<category><![CDATA[TVD$XTAT]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=593</guid>
		<description><![CDATA[Yesterday, I received the following question from a TVD$XTAT user:
XCTEND lines are reported as &#8220;COMMIT/ROLLBACK (synthetic)&#8221;. Using Goolge and Metalink I can&#8217;t find any other resources describing &#8220;COMMIT/ROLLBACK (synthetic)&#8221;. This term seems not be widely used, although Hotsos uses the same term. Could you please elaborate what exactly that is and why it possibly happens?
To [...]]]></description>
			<content:encoded><![CDATA[<p>Yesterday, I received the following question from a <a href="/category/apmtools/tvdxtat/">TVD$XTAT</a> user:</p>
<blockquote><p><em>XCTEND lines are reported as &#8220;COMMIT/ROLLBACK (synthetic)&#8221;. Using Goolge and Metalink I can&#8217;t find any other resources describing &#8220;COMMIT/ROLLBACK (synthetic)&#8221;. This term seems not be widely used, although Hotsos uses the same term. Could you please elaborate what exactly that is and why it possibly happens?</em></p></blockquote>
<p>To understand what &#8220;synthetic&#8221; means, let&#8217;s have a look to two small trace files.The first one is generated by tracing the execution of the following SQL statements in SQL*Plus:</p>
<p><pre>UPDATE scott.emp SET sal = sal*1.15;
COMMIT;</pre></p>
<p>The relevant part of the trace file is the following. Notice that:</p>
<ul>
<li>There are two cursors: the first one is the update, the second one is the commit.</li>
<li>In the second one, because it is a commit, between the PARSE and the EXEC lines there is a XCTEND line. Note that the database engine emmits a XCTEND line for every commit or rollback. To differentiate the two, the attribute &#8220;rlbk&#8221; is used: 0=commit, 1=rollback.</li>
<li>The &#8220;log file sync&#8221; wait is associated to the second cursor, the commit.</li>
</ul>
<p><pre>=====================
PARSING IN CURSOR #3 len=35 dep=0 uid=84 oct=6 lid=84 tim=1250674381587415 hv=950048100 ad=&#039;38e58340&#039; sqlid=&#039;crk1wdnwa15b4&#039;
UPDATE scott.emp SET sal = sal*1.15
END OF STMT
PARSE #3:c=0,e=338,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1494045816,tim=1250674381587404
EXEC #3:c=2999,e=2831,p=0,cr=7,cu=4,mis=0,r=14,dep=0,og=1,plh=1494045816,tim=1250674381590917
STAT #3 id=1 cnt=0 pid=0 pos=1 obj=0 op=&#039;UPDATE  EMP (cr=7 pr=0 pw=0 time=0 us)&#039;
STAT #3 id=2 cnt=14 pid=1 pos=1 obj=73268 op=&#039;TABLE ACCESS FULL EMP (cr=7 pr=0 pw=0 time=14 us cost=3 size=70 card=14)&#039;
WAIT #3: nam=&#039;SQL*Net message to client&#039; ela= 11 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1250674381592362
WAIT #3: nam=&#039;SQL*Net message from client&#039; ela= 1812 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1250674381594288
CLOSE #3:c=0,e=28,dep=0,type=0,tim=1250674381594476
=====================
PARSING IN CURSOR #2 len=6 dep=0 uid=84 oct=44 lid=84 tim=1250674381594857 hv=255718823 ad=&#039;0&#039; sqlid=&#039;8ggw94h7mvxd7&#039;
COMMIT
END OF STMT
PARSE #2:c=0,e=146,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,plh=0,tim=1250674381594814
XCTEND rlbk=0, rd_only=0, tim=1250674381595557
EXEC #2:c=1000,e=740,p=0,cr=0,cu=1,mis=0,r=0,dep=0,og=0,plh=0,tim=1250674381596086
WAIT #2: nam=&#039;log file sync&#039; ela= 1974 buffer#=1980 p2=0 p3=0 obj#=-1 tim=1250674381598390
WAIT #2: nam=&#039;SQL*Net message to client&#039; ela= 0 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1250674381598664
WAIT #2: nam=&#039;SQL*Net message from client&#039; ela= 2770 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1250674381601434
CLOSE #2:c=0,e=17,dep=0,type=3,tim=1250674381601595
=====================</pre></p>
<p>The second trace file is generated by tracing the execution of the same operations from a Java program. The following is the method that contains the update and the commit:</p>
<p><pre>private static void test(Connection connection) throws Exception
{
  Statement statement = connection.createStatement();
  statement.execute(&quot;UPDATE scott.emp SET sal = sal * 1.15&quot;);
  statement.close();
  connection.commit();
}</pre></p>
<p>The relevant part of the trace file is the following. Notice that:</p>
<ul>
<li>There is only one cursor: the update. No cursor related to the commit is available.</li>
<li>Just after the CLOSE line there is a XCTEND line with the attribute &#8220;rlbk&#8221; set to 0. Obviously a commit was performed.</li>
<li>The &#8220;log file sync&#8221; wait is associated to cursor number 0. Note that the database engine associate to cursor number 0 all lines that cannot be associated to other cursors. In this case a cursor with the commit statement is missing, hence it is not possible to associate the wait to it.</li>
</ul>
<p><pre>=====================
PARSING IN CURSOR #2 len=37 dep=0 uid=84 oct=6 lid=84 tim=1250673660406187 hv=517367075 ad=&#039;38d919e4&#039; sqlid=&#039;cc8438wgdct93&#039;
UPDATE scott.emp SET sal = sal * 1.15
END OF STMT
PARSE #2:c=39994,e=40693,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=1494045816,tim=1250673660406167
EXEC #2:c=17997,e=18116,p=0,cr=7,cu=4,mis=0,r=14,dep=0,og=1,plh=1494045816,tim=1250673660427450
STAT #2 id=1 cnt=0 pid=0 pos=1 obj=0 op=&#039;UPDATE  EMP (cr=7 pr=0 pw=0 time=0 us)&#039;
STAT #2 id=2 cnt=14 pid=1 pos=1 obj=73268 op=&#039;TABLE ACCESS FULL EMP (cr=7 pr=0 pw=0 time=5 us cost=3 size=70 card=14)&#039;
WAIT #2: nam=&#039;SQL*Net message to client&#039; ela= 5 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1250673660432401
WAIT #2: nam=&#039;SQL*Net message from client&#039; ela= 12925 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1250673660446291
CLOSE #2:c=0,e=45,dep=0,type=0,tim=1250673660447335
XCTEND rlbk=0, rd_only=0, tim=1250673660449381
WAIT #0: nam=&#039;log file sync&#039; ela= 1325 buffer#=1217 p2=0 p3=0 obj#=-1 tim=1250673660454674
WAIT #0: nam=&#039;SQL*Net message to client&#039; ela= 4 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1250673660454885
=====================</pre></p>
<p>The interesting thing to note about the second case is that a commit was performed without executing a COMMIT statement. This is possible because at the OCI level a commit can be performed by calling the function <a href="http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28395/oci17msc006.htm#LNOCI13112">OCITransCommit</a>. In other words, without having to execute a statement.</p>
<p>Now, back to the question. When TVD$XTAT processes a trace file like the second one, it automatically generates a cursor related to the commit operation. The text of the cursor will be &#8220;COMMIT (synthetic)&#8221;. So, the term &#8220;synthetic&#8221; is only added to point out that it is a generated cursor. In addition, TVD$XTAT also associate the waits associated to the cursor 0 to the generated cursor. This is very important because in some situations, for example when commits are executed very often or when long rollbacks are executed, the time needed for the commit/rollback is not negligible. As a result, if they were not accounted, the unaccounted-for time could be relevant.</p>
<p>BTW, it is not a coincidence that the <a href="http://method-r.com/software/profiler">Method R Profiler</a> (a.k.a. <a href="http://www.hotsos.com/profiler.html">Hotsos Profiler</a>) and TVD$XTAT uses the same term. I fact, while comparing the two profilers, I noticed that in Method R Profiler the generated statements were called “synthetic”. I found the idea good and since I was looking for a method to mark such statements as well, I borrowed their term.</p>
]]></content:encoded>
			<wfw:commentRss>http://antognini.ch/2009/08/synthetic-commits-and-rollbacks/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>TVD$XTAT 4.0 Beta 9</title>
		<link>http://antognini.ch/2009/04/tvdxtat-40-beta-9/</link>
		<comments>http://antognini.ch/2009/04/tvdxtat-40-beta-9/#comments</comments>
		<pubDate>Thu, 09 Apr 2009 07:08:00 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[SQL Trace]]></category>
		<category><![CDATA[TOP]]></category>
		<category><![CDATA[TVD$XTAT]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=189</guid>
		<description><![CDATA[This is just a short note to point out that I just uploaded under the section Downloadable Files of TOP a new version of TVD$XTAT. Not only I introduced some new features, but I also fixed a couple of major bugs related to memory consumption and poor performance&#8230;
The detailed change log since Beta 8 is [...]]]></description>
			<content:encoded><![CDATA[<p>This is just a short note to point out that I just uploaded under the section <a href="/top/downloadable-files/">Downloadable Files</a> of <a href="/top">TOP</a> a new version of <a href="/category/apmtools/tvdxtat/">TVD$XTAT</a>. Not only I introduced some new features, but I also fixed a couple of major bugs related to memory consumption and poor performance&#8230;</p>
<p>The detailed change log since <a href="/2008/11/tvdxtat-40-beta-8/">Beta 8</a> is the following:</p>
<ul>
<li>Added formatting for bind variable values of type DATE</li>
<li>Added support for several execution plans for a single cursor</li>
<li>Added number of executions and hash value to execution plans</li>
<li>Added detection of incomplete execution plans</li>
<li>Added support for RPC bind variables</li>
<li>Added command-line option to control logging level</li>
<li>Added warning for 11.1.0.7 trace files (because of bug# 7522002 timing information might be wrong)</li>
<li>Improved data type detection to distinguish VARCHAR2 from NVARCHAR2 and CHAR from NCHAR</li>
<li>Improved handling of incorrectly formatted input lines</li>
<li>Changed logging formatter (time is displayed with the following pattern HH:mm:ss)</li>
<li>Reduced memory utilization for the processing of large trace files</li>
<li>Fix to prevent poor performance for the processing of large trace files</li>
<li>Fix to replace special characters not supported by XML (the unicode character FFFD is used istead of the special ones)</li>
<li>Fix in template to correctly handle space character in SQL text and bind variable values</li>
<li>Fix to ignore timestamp lines not generated by SQL trace </li>
</ul>
<p>As always, your feedback is welcome!</p>
]]></content:encoded>
			<wfw:commentRss>http://antognini.ch/2009/04/tvdxtat-40-beta-9/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>11g New Feature in DBMS_MONITOR</title>
		<link>http://antognini.ch/2009/02/11g-new-feature-in-dbms_monitor/</link>
		<comments>http://antognini.ch/2009/02/11g-new-feature-in-dbms_monitor/#comments</comments>
		<pubDate>Mon, 02 Feb 2009 23:41:14 +0000</pubDate>
		<dc:creator>Christian Antognini</dc:creator>
				<category><![CDATA[11gR1]]></category>
		<category><![CDATA[SQL Trace]]></category>

		<guid isPermaLink="false">http://antognini.ch/?p=66</guid>
		<description><![CDATA[As of 11g the package DBMS_MONITOR provides an important new feature. The aim of this post is to describe not only what this feature is, but also why it is important.
To illustrate how the new feature works, two things are necessary. First, a small table:
SQL&#62; SELECT * FROM t;

       [...]]]></description>
			<content:encoded><![CDATA[<p>As of 11g the package DBMS_MONITOR provides an important new feature. The aim of this post is to describe not only what this feature is, but also why it is important.</p>
<p>To illustrate how the new feature works, two things are necessary. First, a small table:</p>
<p><pre>SQL&gt; SELECT * FROM t;

         N
----------
         1
         2
         3
         4
         5</pre></p>
<p>Second, an anonymous PL/SQL block. About it notice that:</p>
<ul>
<li>Three queries using the very same cursor are executed.</li>
<li>The number of fetched rows is different for each execution.</li>
<li>Array processing is used to fetch all data in a single call.</li>
<li>The package DBMS_MONITOR is used to enable and disable SQL trace.</li>
</ul>
<p><pre>DECLARE
  l_cursor INTEGER;
  l_n dbms_sql.number_table;
  l_retval INTEGER;
BEGIN
  dbms_monitor.session_trace_enable;
  l_cursor := dbms_sql.open_cursor;
  dbms_sql.parse(l_cursor, &#039;SELECT n FROM t WHERE n &lt;= :1&#039;, 1);
  dbms_sql.define_array(l_cursor, 1, l_n, 5, 1);
  FOR i IN 1..3
  LOOP
    dbms_sql.bind_variable(l_cursor, &#039;:1&#039;, i);
    l_retval := dbms_sql.execute(l_cursor);
    l_retval := dbms_sql.fetch_rows(l_cursor);
  END LOOP;
  dbms_sql.close_cursor(l_cursor);
  dbms_monitor.session_trace_disable;
END;</pre></p>
<p>Now, let&#8217;s start by executing the anonymous PL/SQL block in 10.2.0.4. In that version, the content of the generated trace file is the following. Notice that:</p>
<ul>
<li>One single parse (PARSE line) was performed.</li>
<li>Three executions (EXEC lines) were performed.</li>
<li>Three fetches (FETCH lines) were performed. The first one fetched 1 row (value &#8220;r&#8221;), the second one fetched 2 rows, and the third one fetched 3 rows.</li>
<li>The information about the execution plan (STAT line; notice that I manually removed the runtime statistics and query optimizer estimations to keep the output more readable) was written in the trace file only when the cursor was closed. According to it, 6 rows were fetched (value &#8220;cnt&#8221;). In other words, 1+2+3.</li>
</ul>
<p><pre>PARSING IN CURSOR #18 len=29 dep=1 uid=28 oct=3 lid=28 tim=1204698401458764 hv=2067044879 ad=&#039;72de8330&#039;
SELECT n FROM t WHERE n &lt;= :1
END OF STMT
PARSE #18:c=0,e=427,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,tim=1204698401458760
EXEC #18:c=1000,e=912,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,tim=1204698401509992
FETCH #18:c=0,e=68,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=1,tim=1204698401510109
EXEC #18:c=0,e=12,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,tim=1204698401510184
FETCH #18:c=0,e=26,p=0,cr=3,cu=0,mis=0,r=2,dep=1,og=1,tim=1204698401510234
EXEC #18:c=0,e=11,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,tim=1204698401510290
FETCH #18:c=0,e=19,p=0,cr=3,cu=0,mis=0,r=3,dep=1,og=1,tim=1204698401510333
STAT #18 id=1 cnt=6 pid=0 pos=1 obj=15665 op=&#039;TABLE ACCESS FULL T&#039;</pre></p>
<p>And now, let&#8217;s execute it in 11.1.0.6. In this case notice that:</p>
<ul>
<li>The same number of parse, execute and fetch calls as in 10.2.0.4 were performed.</li>
<li>The information about the execution plan was written in the trace file just after the first execution and <em>not</em> when the statement was closed. For this reason, according to it, only 1 row was fetched.</li>
</ul>
<p><pre>PARSING IN CURSOR #4 len=29 dep=1 uid=30 oct=3 lid=30 tim=1233611735664091 hv=2067044879 ad=&#039;6ae30880&#039; sqlid=&#039;cxa35s1xm96hg&#039;
SELECT n FROM t WHERE n &lt;= :1
END OF STMT
PARSE #4:c=4000,e=118923,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,tim=1233611735664086
EXEC #4:c=1999,e=28275,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,tim=1233611735779067
FETCH #4:c=0,e=88,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=1,tim=1233611735779278
STAT #4 id=1 cnt=1 pid=0 pos=1 obj=17363 op=&#039;TABLE ACCESS FULL T&#039;
EXEC #4:c=0,e=43,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,tim=1233611735795372
FETCH #4:c=0,e=45,p=0,cr=3,cu=0,mis=0,r=2,dep=1,og=1,tim=1233611735795449
EXEC #4:c=0,e=12,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,tim=1233611735795509
FETCH #4:c=0,e=18,p=0,cr=3,cu=0,mis=0,r=3,dep=1,og=1,tim=1233611735795552</pre></p>
<p>Why this difference? </p>
<p>It is because, as of 11g, the procedures in the package dbms_monitor that are used to enable SQL trace accept an additional parameter: PLAN_STAT. As written in the <a href="http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28419/d_monitor.htm#i1003679">documentation</a>, this parameter is used to specify the frequency at which the row source statistics (i.e. information about execution plans) are written in trace files. The accepted values are the following (the default value is NULL):</p>
<ul>
<li>NEVER: no information about the execution plan is written in trace files.</li>
<li>FIRST_EXECUTION (equivalent to NULL): information about the execution plan is written just after the first execution.</li>
<li>ALL_EXECUTIONS: information about the execution plan is written for every execution.</li>
</ul>
<p>Therefore, when the parameter PLAN_STAT is set to ALL_EXECUTIONS, the content of the trace file is the following:</p>
<p><pre>PARSING IN CURSOR #9 len=29 dep=1 uid=30 oct=3 lid=30 tim=1233613010415243 hv=2067044879 ad=&#039;6ae30880&#039; sqlid=&#039;cxa35s1xm96hg&#039;
SELECT n FROM t WHERE n &lt;= :1
END OF STMT
PARSE #9:c=2000,e=28550,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,tim=1233613010415239
EXEC #9:c=2000,e=21361,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,tim=1233613010500308
FETCH #9:c=0,e=70,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=1,tim=1233613010500878
STAT #9 id=1 cnt=1 pid=0 pos=1 obj=17364 op=&#039;TABLE ACCESS FULL T&#039;
EXEC #9:c=999,e=24,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,tim=1233613010506855
FETCH #9:c=0,e=24,p=0,cr=3,cu=0,mis=0,r=2,dep=1,og=1,tim=1233613010506906
STAT #9 id=1 cnt=2 pid=0 pos=1 obj=17364 op=&#039;TABLE ACCESS FULL T&#039;
EXEC #9:c=0,e=15,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,tim=1233613010507425
FETCH #9:c=0,e=27,p=0,cr=3,cu=0,mis=0,r=3,dep=1,og=1,tim=1233613010507479
STAT #9 id=1 cnt=3 pid=0 pos=1 obj=17364 op=&#039;TABLE ACCESS FULL T&#039;</pre></p>
<p>Why is this feature important?</p>
<p>It is because up to 10g, especially for application keeping cursors open for a &#8220;long time&#8221;, it is not unusual to see trace files <em>not</em> containing information about execution plans for every cursor. Since that information is critical to diagnose performance problems, it might be a major issue. As of 11g, however, the trace files should always contain this critical information (except if the value NEVER is used, of course). </p>
]]></content:encoded>
			<wfw:commentRss>http://antognini.ch/2009/02/11g-new-feature-in-dbms_monitor/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

