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

<channel>
	<title>Alexander Anokhin</title>
	<atom:link href="http://alexanderanokhin.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://alexanderanokhin.wordpress.com</link>
	<description>Unique Oracle Stories</description>
	<lastBuildDate>Wed, 22 Feb 2012 07:41:09 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='alexanderanokhin.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://0.gravatar.com/blavatar/06e0ea700e4c113f4b127820af08266a?s=96&#038;d=http%3A%2F%2Fs2.wp.com%2Fi%2Fbuttonw-com.png</url>
		<title>Alexander Anokhin</title>
		<link>http://alexanderanokhin.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://alexanderanokhin.wordpress.com/osd.xml" title="Alexander Anokhin" />
	<atom:link rel='hub' href='http://alexanderanokhin.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Optimized index maintenance in DML</title>
		<link>http://alexanderanokhin.wordpress.com/2011/12/20/index-full-scan-and-array-of-changes-in-dml/</link>
		<comments>http://alexanderanokhin.wordpress.com/2011/12/20/index-full-scan-and-array-of-changes-in-dml/#comments</comments>
		<pubDate>Tue, 20 Dec 2011 14:49:34 +0000</pubDate>
		<dc:creator>Alexander Anokhin</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[CBO]]></category>
		<category><![CDATA[DTrace]]></category>

		<guid isPermaLink="false">http://alexanderanokhin.wordpress.com/?p=316</guid>
		<description><![CDATA[This post is about how amount and type of work during DML can depends on child steps in an execution plan. Introduction Oracle has optimized (this is just my term) index maintenance mechanism used in some kind of DMLs. I call it &#8220;optimized&#8221; because in this case Oracle applies each change to an index as [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=316&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>This post is about how amount and type of work during DML can depends on child steps in an execution plan.</p>
<p><strong>Introduction</strong><br />
Oracle has <em>optimized</em> (this is just my term) index maintenance mechanism used in some kind of DMLs. I call it &#8220;<em>optimized</em>&#8221; because in this case Oracle applies each change to an index as <strong>array</strong> of keys. It is also the reason why you may encounter that a session executing  the simplest statement like this<br />
<pre class="brush: plain;">
delete 
  from t
 where col = :val
</pre><br />
can wait &#8220;<em>direct path write <strong>temp</strong></em>&#8221; / &#8220;<em>direct path read <strong>temp</strong></em>&#8221; because the statement has active workareas not fit into the memory.<br />
Details are below in section &#8220;How it works&#8221;.</p>
<p>Described optimized index maintenance is performed in:<br />
1. Direct-path insert.<br />
2. Delete / Update / Merge if access path to the data is via b-tree Index Range Scan or Index Full Scan.</p>
<p>The first point is better known. When data inserting is finished durnig direct-path insert index maintenance is peformed. But there is no (or almost no) information about the same process during Update/Delete and conditions when it can be performed.<br />
<span id="more-316"></span></p>
<p><em>Note: If you are not interested in internal details you can jump to the section &#8220;How it works&#8221; below.</em></p>
<p>There was a topic on <a title="sql.ru" href="http://www.sql.ru/forum/actualthread.aspx?bid=3&amp;tid=880574">sql.ru</a> where the author asked why Oracle uses Index Fast Full Scan instead of an Index Fast Full Scan on simple DML. Let&#8217;s check it.</p>
<p><strong>Data preparation</strong></p>
<p><pre class="brush: plain;">
SQL&gt; select * from v$version where rownum = 1;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production

SQL&gt; create table t as select * from all_objects;

Table created

SQL&gt; create index i on t(object_id);

Index created

SQL&gt; exec dbms_stats.gather_table_stats(user, 'T', estimate_percent =&gt; 100, cascade =&gt; true);

PL/SQL procedure successfully completed

SQL&gt; select object_name, object_id from user_objects where object_name in ('T', 'I');

OBJECT_NAME      OBJECT_ID
--------------- ----------
I                    81755
T                    81754
</pre></p>
<p>Let&#8217;s check what Oracle thinks about following statement</p>
<p><pre class="brush: plain;">
  delete
    from t
   where object_id &lt;&gt; 0;
</pre></p>
<p>&nbsp;</p>
<p><pre class="brush: plain;">
SQL&gt; explain plan for
  2    delete
  3      from t
  4     where object_id &lt;&gt; 0;

Explained

SQL&gt; @plan

-------------------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | DELETE STATEMENT |      | 65919 |   321K|   149   (2)| 00:00:02 |
|   1 |  DELETE          | T    |       |       |            |          |
|*  2 |   INDEX FULL SCAN| I    | 65919 |   321K|   149   (2)| 00:00:02 |
-------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(&quot;OBJECT_ID&quot;&lt;&gt;0)
</pre></p>
<p>So, Oracle has choosen Index Full Scan.</p>
<p>Let&#8217;s check what Oracle thinks about Index Fast Full Scan</p>
<p><pre class="brush: plain;">
SQL&gt; explain plan for
  2    delete --+ index_ffs(t i)
  3      from t
  4     where object_id &lt;&gt; 0;

Explained

SQL&gt; @plan;

------------------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | DELETE STATEMENT      |      | 65919 |   321K|    35   (3)| 00:00:01 |
|   1 |  DELETE               | T    |       |       |            |          |
|*  2 |   INDEX FAST FULL SCAN| I    | 65919 |   321K|    35   (3)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(&quot;OBJECT_ID&quot;&lt;&gt;0)
</pre></p>
<p>The cost in IFFS case is 35 when the cost in IFS case is 149.<br />
If we check 10053 we will see than Index Fast Full Scan is not considered at all.<br />
Looks like CBO oddity.</p>
<p>Let&#8217;s execute both statements.</p>
<p><pre class="brush: plain;">
SQL&gt; begin
  2
  3      dbms_monitor.session_trace_enable;
  4
  5      delete
  6        from t
  7       where object_id &lt;&gt; 0;
  8
  9      rollback;
 10
 11      delete --+ index_ffs(t i)
 12        from t
 13       where object_id &lt;&gt; 0;
 14
 15      rollback;
 16
 17  end;
 18  /

PL/SQL procedure successfully completed
</pre></p>
<p>&nbsp;</p>
<p><pre class="brush: plain;">
DELETE FROM T
WHERE
 OBJECT_ID &lt;&gt; 0

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      2.15       3.16       2135        155      77120       65920
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      2.15       3.16       2135        155      77120       65920

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 9511     (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
      0  DELETE  T (cr=155 pr=2135 pw=0 time=0 us)
  65920   INDEX FULL SCAN I (cr=147 pr=0 pw=0 time=0 us cost=149 size=329595 card=65919)(object id 81755)

....

DELETE --+ index_ffs(t i)
 FROM T WHERE OBJECT_ID &lt;&gt; 0

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      3.42       4.61       2697        165     210070       65920
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      3.42       4.61       2697        165     210070       65920

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 9511     (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
      0  DELETE  T (cr=165 pr=2697 pw=0 time=0 us)
  65920   INDEX FAST FULL SCAN I (cr=153 pr=0 pw=0 time=0 us cost=35 size=329595 card=65919)(object id 81755)
</pre></p>
<p>Pay attention 77120 current gets in Index Full Scan case and 210070 current gets in Index Fast Full Scan case.<br />
What is going on?</p>
<p>The answer we can found in redo log dump (similar info you can find in undo changes dump, event 10221).<br />
But before let&#8217;s look on the <a title="dtracelio" href="http://alexanderanokhin.wordpress.com/scripts/dtracelio-d/">dtracelio</a> output. Note that current version of dtracelio contains mode_held for current gets (db block gets), also it monitors calls of function kcbget which also increment statistic &#8220;db block gets&#8221; and calculated in &#8220;cu=&#8221; figures in 10046 trace.</p>
<p><strong>DtraceLIO</strong><br />
Below is an excerpt from Index Full Scan case. The most part of the output (~99%) looks like following (it is only small excerpt):</p>
<p><pre class="brush: plain;">
...
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec7 (5/69319) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec7 (5/69319) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec7 (5/69319) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec7 (5/69319) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec7 (5/69319) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec7 (5/69319) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec7 (5/69319) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF5C48,2,258,0) [tsn: 5 rdba: 0x1410e81 (5/69249) obj: 81754 dobj: 81754] where: 258 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec8 (5/69320) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec8 (5/69320) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec8 (5/69320) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec8 (5/69320) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ec8 (5/69320) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF5C48,2,258,0) [tsn: 5 rdba: 0x1410e81 (5/69249) obj: 81754 dobj: 81754] where: 258 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ee0 (5/69344) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ee0 (5/69344) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ee0 (5/69344) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ee0 (5/69344) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF5C48,2,258,0) [tsn: 5 rdba: 0x1410e81 (5/69249) obj: 81754 dobj: 81754] where: 258 mode_held: 2
kcbgcur(0xFFFFFFFF7CE5A4E8,2,952,0) [tsn: 5 rdba: 0x1410ee1 (5/69345) obj: 81754 dobj: 81754] where: 952 mode_held: 2
...
</pre></p>
<p>Object 81754 is the table.<br />
We can see here<br />
* blocks 5/69319, 5/69320, 5/69344, 5/69345 (table data blocks) are read by kcbgcur calls from where: 952 (&#8220;kddwh01: kdddel&#8221;, delete row)<br />
* block 5/69249 (first level bitmap block, ASSM) is read by kcbgcur calls from where: 258 is &#8220;ktspfwh12: ktspstchg&#8221;</p>
<p>These calls delete rows from the table.</p>
<p>after that:</p>
<p><pre class="brush: plain;">
...
kcbgcur(0xFFFFFFFF7FFF5258,1,816,0) [tsn: 5 rdba: 0x140281b (5/10267) obj: 81755 dobj: 81755] where: 816 mode_held: 1
kcbget(0xFFFFFFFF7FFF5258,2,820,0) [tsn: 5 rdba: 0x140281c (5/10268) obj: 81755 dobj: 81755] where: 820 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF2C30,2,38,0) [tsn: 2 rdba: 0xc00100 (3/256) obj: 0 dobj: -1] where: 38 mode_held: 2
kcbgcur(0x461B36078,2,10,0) [tsn: 2 rdba: 0xc81bc9 (3/531401) obj: 0 dobj: -1] where: 10 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF5258,1,816,0) [tsn: 5 rdba: 0x140281b (5/10267) obj: 81755 dobj: 81755] where: 816 mode_held: 1
kcbget(0xFFFFFFFF7FFF5258,2,820,0) [tsn: 5 rdba: 0x140281c (5/10268) obj: 81755 dobj: 81755] where: 820 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF3498,2,258,0) [tsn: 5 rdba: 0x1402818 (5/10264) obj: 81755 dobj: 81755] where: 258 mode_held: 2
...
</pre></p>
<p>Object 81755 is the index.<br />
* Block 5/10267 (index root block) is read four times by kcbgcur calls in SHARED mode (mode_held = 1) from where: 816 is &#8220;kdiwh17: kdifind&#8221; (Finds the appropriate index block to store the key ?).<br />
* Blocks 5/10268 and 5/10269 (index leaf blocks) are read by kcbget in EXCLUSIVE mode (mode_held = 2) from where: 820 is &#8220;kdiwh22: kdifind&#8221;.<br />
* Block 5/10264 (first level bitmap block, ASSM) is read from where: 258 is &#8220;ktspfwh12: ktspstchg&#8221;.</p>
<p><em>Note:<br />
the index in the example has blevel=1, the only root block and leaf blocks. Below I show that all branches are also read before each leaf block.</em></p>
<p>Summary section:</p>
<p><pre class="brush: plain;">
=============================== Summary =========================
object_id    data_object_id  function   mode_held  where    count
81755        81755           kcbgtcr               1048     1
81755        81755           kcbgtcr               1047     1
0            -1              kcbgcur    2          168      2
0            -1              kcbgtcr               643      2
1            -1              kcbgcur    1          744      2
1            -1              kcbgcur    2          715      2
1            -1              kcbgcur    2          745      2
17           17              kcbgtcr               765      2
44           44              kcbgtcr               815      2
44           44              kcbgtcr               1047     2
0            -1              kcbgcur    1          48       4
0            -1              kcbgcur    1          39       6
81754        81754           kcbgcur    2          325      63
81755        81755           kcbgtcr               814      145
81755        81755           kcbgcur    2          258      146
81755        81755           kcbgcur    1          816      291
81755        81755           kcbget     2          820      291
0            -1              kcbgcur    2          10       2134
0            -1              kcbgcur    2          38       2139
81754        81754           kcbgcur    2          258      3975
81754        81754           kcbgcur    2          952      65922
</pre></p>
<p>We can see here<br />
65922 calls of kcbgcur from where: 952 (&#8220;kddwh01: kdddel&#8221; delete row)<br />
291 calls of kcbgcur from where: 816 in shared mode (index root block)<br />
291 calls of kcbget from where: 820 in exclusive mode (leaf blocks)</p>
<p>Index Fast Full Scan:<br />
The most part of output looks following:</p>
<p><pre class="brush: plain;">
...
kcbgcur(0xFFFFFFFF7FFF67F8,1,816,0) [tsn: 5 rdba: 0x140281b (5/10267) obj: 81755 dobj: 81755] where: 816 mode_held: 1
kcbget(0xFFFFFFFF7FFF67F8,2,820,0) [tsn: 5 rdba: 0x140281c (5/10268) obj: 81755 dobj: 81755] where: 820 mode_held: 2
kcbgcur(0xFFFFFFFF7CE6BFB0,2,952,0) [tsn: 5 rdba: 0x140272b (5/10027) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF67F8,1,816,0) [tsn: 5 rdba: 0x140281b (5/10267) obj: 81755 dobj: 81755] where: 816 mode_held: 1
kcbget(0xFFFFFFFF7FFF67F8,2,820,0) [tsn: 5 rdba: 0x140281c (5/10268) obj: 81755 dobj: 81755] where: 820 mode_held: 2
kcbgcur(0xFFFFFFFF7CE6BFB0,2,952,0) [tsn: 5 rdba: 0x140272b (5/10027) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF67F8,1,816,0) [tsn: 5 rdba: 0x140281b (5/10267) obj: 81755 dobj: 81755] where: 816 mode_held: 1
kcbget(0xFFFFFFFF7FFF67F8,2,820,0) [tsn: 5 rdba: 0x140281c (5/10268) obj: 81755 dobj: 81755] where: 820 mode_held: 2
kcbgcur(0xFFFFFFFF7CE6BFB0,2,952,0) [tsn: 5 rdba: 0x140272b (5/10027) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF67F8,1,816,0) [tsn: 5 rdba: 0x140281b (5/10267) obj: 81755 dobj: 81755] where: 816 mode_held: 1
kcbget(0xFFFFFFFF7FFF67F8,2,820,0) [tsn: 5 rdba: 0x140281c (5/10268) obj: 81755 dobj: 81755] where: 820 mode_held: 2
kcbgcur(0xFFFFFFFF7CE6BFB0,2,952,0) [tsn: 5 rdba: 0x140272b (5/10027) obj: 81754 dobj: 81754] where: 952 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF67F8,1,816,0) [tsn: 5 rdba: 0x140281b (5/10267) obj: 81755 dobj: 81755] where: 816 mode_held: 1
kcbget(0xFFFFFFFF7FFF67F8,2,820,0) [tsn: 5 rdba: 0x140281c (5/10268) obj: 81755 dobj: 81755] where: 820 mode_held: 2
kcbgcur(0xFFFFFFFF7CE6BFB0,2,952,0) [tsn: 5 rdba: 0x140272b (5/10027) obj: 81754 dobj: 81754] where: 952 mode_held: 2
...
</pre></p>
<p>We can see here<br />
block 5/10267 (index root block) is read by kcbgcur calls from where: 816 in shared mode.<br />
block 5/10268 (index leaf) is read by kcbget calls from where: 820 in exclusive mode.<br />
block 5/10027 (table data block) is read by kcbgcur calls from where: 952 (kddwh01: kdddel, delete row) in exclusive mode.</p>
<p><pre class="brush: plain;">
=============================== Summary =========================
object_id    data_object_id  function   mode_held  where    count
0            -1              kcbgcur    2          6        1
81755        81755           kcbgtcr               644      2
0            -1              kcbgcur    2          168      3
0            -1              kcbgtcr               643      3
1            -1              kcbgcur    1          744      3
1            -1              kcbgcur    2          715      3
1            -1              kcbgcur    2          745      3
17           17              kcbgtcr               765      3
44           44              kcbgtcr               815      3
44           44              kcbgtcr               1047     3
81755        81755           kcbgtcr               645      4
0            -1              kcbgcur    1          48       5
0            -1              kcbgcur    1          39       8
81754        81754           kcbgcur    2          325      63
81755        81755           kcbgcur    2          258      146
81755        81755           kcbgtcr               872      147
0            -1              kcbgcur    2          10       2696
0            -1              kcbgcur    2          38       2703
81754        81754           kcbgcur    2          258      3975
81755        81755           kcbgcur    1          816      65921
81755        81755           kcbget     2          820      65921
81754        81754           kcbgcur    2          952      65922
</pre></p>
<p>We can see here<br />
65921 calls of kcbgcur from where: 816 (index root blocks were read by these calls)<br />
65921 calls of kcbgcur from where: 820 (index leaf blocks were read by these calls)<br />
65921 calls of kcbgcur from where: 952 (table data block were read by these calls)</p>
<p><strong>Redo log dump</strong><br />
And below is an excerpt containing &#8220;index undo for leaf key operations&#8221; from redo log dump:</p>
<p>Index Full Scan</p>
<p><pre class="brush: plain;">
REDO RECORD - Thread:1 RBA: 0x00002c.0000c7af.0118 LEN: 0x0ff8 VLD: 0x01
SCN: 0x09d8.6f140f23 SUBSCN:  1 12/20/2011 18:13:04
CHANGE #1 TYP:0 CLS:35 AFN:3 DBA:0x00c00110 OBJ:4294967295 SCN:0x09d8.6f140f21 SEQ:  1 OP:5.2 ENC:0
ktudh redo: slt: 0x001f sqn: 0x00000000 flg: 0x000a siz: 3380 fbi: 0
            uba: 0x00c82c4b.0392.01    pxid:  0x0000.000.00000000
CHANGE #2 TYP:1 CLS:36 AFN:3 DBA:0x00c82c4b OBJ:4294967295 SCN:0x09d8.6f140f22 SEQ:  1 OP:5.1 ENC:0
ktudb redo: siz: 3380 spc: 1238 flg: 0x000a seq: 0x0392 rec: 0x01
            xid:  0x000a.01f.00014438
ktubu redo: slt: 31 rci: 0 opc: 10.22 objn: 81755 objd: 81755 tsn: 5
Undo type:  Regular undo       Undo type:  Last buffer split:  No
Tablespace Undo:  No
             0x00c82c4a
index undo for leaf key operations
KTB Redo
op: 0x04  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: L  itl: xid:  0xffff.000.00000000 uba: 0x00000000.0000.00
                      flg: C---    lkc:  0     scn: 0x09d8.6f13d63f
Dump kdilk : itl=2, kdxlkflg=0x25 sdc=0 indexid=0x140281a block=0x0140281c
(kdxlre): restore leaf row (clear leaf delete flags)
number of keys: 255
key sizes:
 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
 10 10 10 10 10 10 10 10 10 10 10 10 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11
 11 11 11 11 11 11 11 11 11 11 11 11 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11
key :(2705):
 02 c1 03 06 01 40 27 2b 00 1a 02 c1 04 06 01 40 27 2b 00 04 02 c1 05 06 01
 40 27 2b 00 1f 02 c1 06 06 01 40 27 2b 00 16 02 c1 07 06 01 40 27 2b 00 03
 02 c1 08 06 01 40 27 2b 00 12 02 c1 09 06 01 40 27 2b 00 2e 02 c1 0a 06 01
 40 27 2b 00 20 02 c1 0b 06 01 40 27 2b 00 37 02 c1 0c 06 01 40 27 2b 00 02
...
selflock: (32):
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00
bitmap: (32):
 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
 ff ff ff ff ff ff ff
CHANGE #3 TYP:0 CLS: 1 AFN:5 DBA:0x0140281c OBJ:81755 SCN:0x09d8.6f140727 SEQ:232 OP:10.4 ENC:0
index redo (kdxlde):  delete leaf row
KTB Redo
op: 0x01  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: F  xid:  0x000a.01f.00014438    uba: 0x00c82c4b.0392.01
REDO: ARRAY / -- / --
itl: 2, sno: 0, row size 3725
number of keys: 255
slots:
 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 12
 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
</pre></p>
<p>Pay attention, CHANGE #2 (index undo) and CHANGE #3 (index redo) contain ARRAY of changes, 255 index keys in each change.</p>
<p>Index Fast Full Scan</p>
<p><pre class="brush: plain;">
REDO RECORD - Thread:1 RBA: 0x00002e.00000005.0020 LEN: 0x00e8 VLD: 0x01
SCN: 0x09d8.6f141196 SUBSCN:  1 12/20/2011 18:20:45
CHANGE #1 TYP:0 CLS:36 AFN:3 DBA:0x00c82d1f OBJ:4294967295 SCN:0x09d8.6f141195 SEQ:  1 OP:5.1 ENC:0
ktudb redo: siz: 100 spc: 3982 flg: 0x0022 seq: 0x0392 rec: 0x1d
            xid:  0x000a.019.00014416
ktubu redo: slt: 25 rci: 28 opc: 10.22 objn: 81755 objd: 81755 tsn: 5
Undo type:  Regular undo       Undo type:  Last buffer split:  No
Tablespace Undo:  No
             0x00000000
index undo for leaf key operations
KTB Redo
op: 0x04  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: L  itl: xid:  0xffff.000.00000000 uba: 0x00000000.0000.00
                      flg: C---    lkc:  0     scn: 0x09d8.6f13d63f
Dump kdilk : itl=2, kdxlkflg=0x1 sdc=0 indexid=0x140281a block=0x0140281c
(kdxlre): restore leaf row (clear leaf delete flags)
key :(10):  02 c1 03 06 01 40 27 2b 00 1a
CHANGE #2 TYP:0 CLS: 1 AFN:5 DBA:0x0140281c OBJ:81755 SCN:0x09d8.6f140fbb SEQ:  2 OP:10.4 ENC:0
index redo (kdxlde):  delete leaf row
KTB Redo
op: 0x01  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: F  xid:  0x000a.019.00014416    uba: 0x00c82d1f.0392.1d
REDO: SINGLE / -- / --
itl: 2, sno: 0, row size 14

...

REDO RECORD - Thread:1 RBA: 0x00002e.00000006.007c LEN: 0x00d0 VLD: 0x01
SCN: 0x09d8.6f141196 SUBSCN:  3 12/20/2011 18:20:45
CHANGE #1 TYP:0 CLS:36 AFN:3 DBA:0x00c82d1f OBJ:4294967295 SCN:0x09d8.6f141196 SEQ:  2 OP:5.1 ENC:0
ktudb redo: siz: 84 spc: 3662 flg: 0x0022 seq: 0x0392 rec: 0x1f
            xid:  0x000a.019.00014416
ktubu redo: slt: 25 rci: 30 opc: 10.22 objn: 81755 objd: 81755 tsn: 5
Undo type:  Regular undo       Undo type:  Last buffer split:  No
Tablespace Undo:  No
             0x00000000
index undo for leaf key operations
KTB Redo
op: 0x02  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: C  uba: 0x00c82d1f.0392.1d
Dump kdilk : itl=2, kdxlkflg=0x1 sdc=0 indexid=0x140281a block=0x0140281c
(kdxlre): restore leaf row (clear leaf delete flags)
key :(10):  02 c1 04 06 01 40 27 2b 00 04
CHANGE #2 TYP:0 CLS: 1 AFN:5 DBA:0x0140281c OBJ:81755 SCN:0x09d8.6f141196 SEQ:  1 OP:10.4 ENC:0
index redo (kdxlde):  delete leaf row
KTB Redo
op: 0x02  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: C  uba: 0x00c82d1f.0392.1f
REDO: SINGLE / -- / --
itl: 2, sno: 1, row size 14
</pre></p>
<p>Here each change contains only one index key.</p>
<p>As we can see in the IFS case Oracle is able to delete index keys by a few calls as ARRAY.<br />
And in the IFFS case Oracle deletes index keys as SINGLE records, one by one.</p>
<p><strong>Conclusion</strong><br />
So, the conclusion &#8211; probably it is not CBO bug, but feature, heuristic <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p><strong>How it works:</strong><br />
Workarea with operation_type &#8220;IDX MAINTENANCE (SORT)&#8221; where index keys are sorted and stored is created for <strong>each</strong> index (btree, non-reverse) whose keys are changed by the DML. So, number of workareas will be equivalent number of appropriate indexes. After data changes is finished index keys from workareas are applied by ARRAYs to the existent indexes.<br />
Thus, be careful, if Oracle does not have required PGA for these workareas these sortings will be spilled on the disk.</p>
<p>Described optimization, ARRAY redo/undo, is applied to ALL appropriate (b-tree, non-reverse) indexes which are changed during DML if access path to changed table is via Index Full/Range Scan.<br />
Let&#8217;s look at the the first example:<br />
<pre class="brush: plain;">
SQL&gt; explain plan for
  2    delete
  3      from t
  4     where object_id &lt;&gt; 0;

Explained

SQL&gt; @plan

-------------------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | DELETE STATEMENT |      | 65919 |   321K|   149   (2)| 00:00:02 |
|   1 |  DELETE          | T    |       |       |            |          |
|*  2 |   INDEX FULL SCAN| IDX  | 65919 |   321K|   149   (2)| 00:00:02 |
-------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(&quot;OBJECT_ID&quot;&lt;&gt;0)
</pre></p>
<p>Let&#8217;s suggest that index IDX is a reverse index. In this case ARRAY optimization is not applied for this exact index.<br />
All redo/undo changes will be SINGLE, row by row. As in Full Table Scan or Index Fast Full Scan cases.</p>
<p>But what if there are N more normal b-tree indexes on the table T?<br />
In this case ARRAY optimization will be applied for all these indexes except reverse index IDX, although the execution plan contains Index Full Scan on reverse index IDX.</p>
<p><strong>Additional points</strong><br />
I would like to emphasize some points which are described in this post</p>
<p>1. I&#8217;ve learnt that Jonathan Lewis wrote about this phenomenon in 2006(!)<br />
<a title="tuning-updates" href="http://jonathanlewis.wordpress.com/2006/11/22/tuning-updates/">http://jonathanlewis.wordpress.com/2006/11/22/tuning-updates/</a></p>
<p>2. Note, how index blocks are read during DML.<br />
Before leaf block is read for change (to change a key from kdifind, in fact you can see many another block gets especially during update statement) index root block and all index branch blocks above that leaf are also read.<br />
Without ARRAY optimization (Full Table Scan, Index Fast Full Scan, reverse index, IOT, etc) it is caused for each row change.<br />
And with ARRAY optimization it is caused (leaf block read) for each array batch operation.<br />
This is especially important for indexes with high blevel.</p>
<p>The index in the example above has blevel=1, so the only root block and leaf blocks.<br />
Let&#8217;s look at an excerpt of dtracelio output of the similar index changes in index with blevel=4.<br />
Here we can see two array changes for two leaf blocks.<br />
<pre class="brush: plain;">
...
kcbgcur(0xFFFFFFFF7FFF5258,1,816,0) [tsn: 31 rdba: 0x1e8a32c4 (122/668356) obj: 82756 dobj: 82756] where: 816 mode_held: 1
kcbget(0xFFFFFFFF7FFF5258,1,820,0) [tsn: 31 rdba: 0x1e8a328c (122/668300) obj: 82756 dobj: 82756] where: 820 mode_held: 1
kcbget(0xFFFFFFFF7FFF5258,1,820,0) [tsn: 31 rdba: 0x1d894f8e (118/610190) obj: 82756 dobj: 82756] where: 820 mode_held: 1
kcbget(0xFFFFFFFF7FFF5258,1,820,0) [tsn: 31 rdba: 0x1f09a0ec (124/631020) obj: 82756 dobj: 82756] where: 820 mode_held: 1
kcbget(0xFFFFFFFF7FFF5258,2,820,0) [tsn: 31 rdba: 0x1e8a328f (122/668303) obj: 82756 dobj: 82756] where: 820 mode_held: 2
kcbgcur(0xFFFFFFFF7FFF5258,1,816,0) [tsn: 31 rdba: 0x1e8a32c4 (122/668356) obj: 82756 dobj: 82756] where: 816 mode_held: 1
kcbget(0xFFFFFFFF7FFF5258,1,820,0) [tsn: 31 rdba: 0x1e8a328c (122/668300) obj: 82756 dobj: 82756] where: 820 mode_held: 1
kcbget(0xFFFFFFFF7FFF5258,1,820,0) [tsn: 31 rdba: 0x1d894f8e (118/610190) obj: 82756 dobj: 82756] where: 820 mode_held: 1
kcbget(0xFFFFFFFF7FFF5258,1,820,0) [tsn: 31 rdba: 0x1f09a0ec (124/631020) obj: 82756 dobj: 82756] where: 820 mode_held: 1
kcbget(0xFFFFFFFF7FFF5258,2,820,0) [tsn: 31 rdba: 0x1f09a0e0 (124/631008) obj: 82756 dobj: 82756] where: 820 mode_held: 2
...
</pre><br />
The first 5 gets in excerpt:<br />
Block (122/668356) &#8211; index root block &#8211; was read by kcbgcur from &#8220;where: 816&#8243; in shared mode<br />
Blocks (122/668300), (118/610190), (124/631020) &#8211; index branches &#8211; were read by kcbget from &#8220;where: 820&#8243; in shared mode<br />
Block (122/668303) &#8211; index leaf block &#8211; was read by kcbgcur from &#8220;where: 820&#8243; in exclusive mode<br />
And after that, the second 5 gets, the same root block and the same branches, but another leaf block (124/631008).</p>
<p>In the case without array optimization these gets (root, branches, leaf) will be read for each index key change.</p>
<p>3. The optimization is not performed on IOTs itself, but performed on additional indexes on IOT.</p>
<p>4. It does not work for whole DML when access path is Index Range/Full Scan <strong>Descending</strong> (as in an example below),<br />
<pre class="brush: plain;">
SQL&gt; explain plan for
  2      delete --+ index_desc(t)
  3        from t
  4       where object_id &lt;&gt; 0;
 
Explained
 
SQL&gt; @planb
--------------------------------------------
| Id  | Operation                   | Name |
--------------------------------------------
|   0 | DELETE STATEMENT            |      |
|   1 |  DELETE                     | T    |
|   2 |   INDEX FULL SCAN DESCENDING| IDX  |
--------------------------------------------
</pre><br />
but works with Index Range/Full Scan [ASC] on an index created with DESC sorting order.</p>
<p>5. It also does not work in the following case:<br />
<pre class="brush: plain;">
SQL&gt; explain plan for
  2      delete --+ all_rows
  3        from t
  4       where object_id &lt;&gt; 0
  5         and rownum &lt; 999999999999;   
Explained   

SQL&gt; @plan
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | DELETE STATEMENT  |      | 65919 |   321K|   149   (2)| 00:00:02 |
|   1 |  DELETE           | T    |       |       |            |          |
|*  2 |   COUNT STOPKEY   |      |       |       |            |          |
|*  3 |    INDEX FULL SCAN| I    | 65919 |   321K|   149   (2)| 00:00:02 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(ROWNUM   3 - filter(&quot;OBJECT_ID&quot;&lt;&gt;0)
</pre></p>
<p>6. From the other hand this optimization works in more complicated execution plans as following cases:<br />
<pre class="brush: plain;">
delete
  from t1
 where exists (select 1 from t2 where t2.id = t1.id)
</pre></p>
<p>&nbsp;</p>
<p><pre class="brush: plain;">
---------------------------------------
| Id  | Operation              | Name |
---------------------------------------
|   0 | DELETE STATEMENT       |      |
|   1 |  DELETE                | T1   |
|   2 |   HASH JOIN SEMI       |      |
|   3 |    INDEX FULL SCAN     | PK1  |
|   4 |    INDEX FAST FULL SCAN| PK2  |
---------------------------------------
</pre></p>
<p>&nbsp;</p>
<p><pre class="brush: plain;">
------------------------------------
| Id  | Operation           | Name |
------------------------------------
|   0 | DELETE STATEMENT    |      |
|   1 |  DELETE             | T1   |
|   2 |   NESTED LOOPS SEMI |      |
|   3 |    INDEX FULL SCAN  | PK1  |
|   4 |    INDEX UNIQUE SCAN| PK2  |
------------------------------------
</pre></p>
<p><strong>P.S.</strong><br />
Btw, are you aware that there is Dtrace for Oracle Linux ( <a title="DTrace for Oracle Linux" href="http://blogs.oracle.com/wim/entry/trying_out_dtrace">http://blogs.oracle.com/wim/entry/trying_out_dtrace</a> )?<br />
I have not touched yet, you have a chance, enjoy! <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/alexanderanokhin.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/alexanderanokhin.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/alexanderanokhin.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/alexanderanokhin.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/alexanderanokhin.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/alexanderanokhin.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/alexanderanokhin.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/alexanderanokhin.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/alexanderanokhin.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/alexanderanokhin.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/alexanderanokhin.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/alexanderanokhin.wordpress.com/316/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/alexanderanokhin.wordpress.com/316/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/alexanderanokhin.wordpress.com/316/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=316&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://alexanderanokhin.wordpress.com/2011/12/20/index-full-scan-and-array-of-changes-in-dml/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/48a7cd21f3eee64ace66d6668a3e1223?s=96&#38;d=http%3A%2F%2Fs0.wp.com%2Fi%2Fmu.gif&#38;r=G" medium="image">
			<media:title type="html">alexanderanokhin</media:title>
		</media:content>
	</item>
		<item>
		<title>Dynamic tracing of Oracle logical I/O</title>
		<link>http://alexanderanokhin.wordpress.com/2011/11/13/dynamic-tracing-of-oracle-logical-io/</link>
		<comments>http://alexanderanokhin.wordpress.com/2011/11/13/dynamic-tracing-of-oracle-logical-io/#comments</comments>
		<pubDate>Sun, 13 Nov 2011 03:01:47 +0000</pubDate>
		<dc:creator>Alexander Anokhin</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[DTrace]]></category>

		<guid isPermaLink="false">http://alexanderanokhin.wordpress.com/?p=224</guid>
		<description><![CDATA[I would like to provide one more option to investigate Oracle logical I/O with DTrace. This method allows to see each consistent/current gets with details about block, object and location from which function is called. This is the DTrace script: dtracelio.d Short description: This tool allows to see: &#8211; details of each call (consistent/current get) [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=224&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I would like to provide one more option to investigate Oracle logical I/O with DTrace.<br />
This method allows to see each consistent/current gets with details about block, object and location from which function is called. </p>
<p>This is the DTrace script: <a href="http://alexanderanokhin.wordpress.com/scripts/dtracelio-d/">dtracelio.d</a></p>
<p><strong>Short description</strong>:<br />
This tool allows to see:<br />
 &#8211; details of each call (consistent/current get)<br />
 &#8211; details of blocks been read<br />
 &#8211; function from which call been performed without investigating of call stack (&#8220;where&#8221;)<br />
 &#8211; aggregation of performed calls</p>
<p>The tool can be especially helpful in 11g because x$kcbuwhy (x$kcbsw in 10g) is not populated properly there.<br />
<em>Note: Examples of using views x$kcbsw/x$kcbuwhy:<br />
- <a href="http://www.jlcomp.demon.co.uk/buffer_usage.html">Investigating Logical I/O by Jonathan Lewis</a><br />
- <a href="http://blog.tanelpoder.com/2009/11/19/finding-the-reasons-for-excessive-logical-ios/">Finding the reasons for excessive logical IOs by Tanel Poder</a><br />
</em></p>
<p>Current version of the script monitors execution of two Kernel Cache Layer functions:<br />
kcbgtcr &#8211; Kernel Cache Buffer Get Consistent Read. Number of calls of this function we can see as statistic &#8220;consistent gets&#8221; and in 10046 trace as &#8220;cr=&#8221;.<br />
kcbgcur &#8211; Kernel Cache Buffer Get Current Read. Number of calls of this function we can see as statistic &#8220;db block gets&#8221; in 10046 trace as &#8220;cu=&#8221;.<br />
<em>upd:<br />
statistic &#8220;db block gets&#8221; is incremented not only by kcbgcur, but also by kcbget (included in current version of <a href="http://alexanderanokhin.wordpress.com/scripts/dtracelio-d/">dtracelio.d</a>), kcbnew (coming soon), and some others. But kcbgcur is most often used.<br />
</em></p>
<p>In the both functions:<br />
- the first argument is a pointer on the structure which describes a block;<br />
- the second argument is unknown, but I could suggest that this is lock_mode (from MOS bug 7109078);<br />
- the third argument (the least significant bits) is &#8220;where&#8221; (x$kcbwh), that is a module where the function is called. </p>
<p><strong>Usage:</strong><br />
dtracelio.d PID [show_each_call]</p>
<p>PID &#8211; unix process ID<br />
show_each_call &#8211; if 0 then output of each call will be disabled, else details of each call will be shown</p>
<p>A little example of usage. Let&#8217;s try to see what is going on during select on very small table:</p>
<p><pre class="brush: plain;">
SQL&gt; @spid
 
       SID ORACLE_DEDICATED_PROCESS CLIENTPID
---------- ------------------------ ------------------------
        41 18949                    900:408
 
SQL&gt; create table dualcopy as select * from dual;
 
Table created
 
SQL&gt; select * from dualcopy;
 
DUMMY
-----
X

</pre><br />
Notice, that I have executed &#8220;select * from dualcopy&#8221; to avoid hard parsing in consequences executions.</p>
<p>Let&#8217;s execute dtracelio<br />
<pre class="brush: plain;">
dtracelio.d 18949
</pre><br />
I have executed the script with default parameter show_each_call, it means that details of each calls will be shown.</p>
<p>Now I am executing our query:<br />
<pre class="brush: plain;">
SQL&gt; select * from dualcopy;
 
DUMMY
-----
X
</pre></p>
<p>And this is an output of dtracelio<br />
<pre class="brush: plain;">
kcbgtcr(0xFFFFFD7FFFDFB0F0,0,742,0) [tsn: 4 rdba: 0x100060a (4/1546) obj: 79218 dobj: 79218] where: 742
kcbgtcr(0xFFFFFD7FFFDFAED0,0,743,0) [tsn: 4 rdba: 0x100060a (4/1546) obj: 79218 dobj: 79218] where: 743
kcbgtcr(0xFFFFFD7FFDC3B2E0,0,860,0) [tsn: 4 rdba: 0x100060b (4/1547) obj: 79218 dobj: 79218] where: 860
</pre></p>
<p>You can see here:<br />
kcbgtcr(0xFFFFFD7FFFDFB0F0,0,742,0) &#8211; it is function call with 3 arguments,<br />
tsn: 4 &#8211; a tablespace number, ts# from v$tablespace<br />
rdba: 0x100060a &#8211; a relative dba (data block address)<br />
(4/1546) &#8211; file 4 block 1546<br />
obj: 79218 &#8211; dictionary object number, object_id from dba_objects<br />
dobj: 79218 &#8211; data object number, data_object_id from dba_objects<br />
where: 742 &#8211; location from function (kcbgtcr in this case) was executed. This is INDX from x$kcbwf.</p>
<p><pre class="brush: plain;">
SQL&gt; select indx, kcbwhdes from x$kcbwh where indx in (742, 743, 860);
 
      INDX KCBWHDES
---------- ----------------------------------------------------------------
       742 ktewh25: kteinicnt
       743 ktewh26: kteinpscan
       860 kdswh01: kdstgr
</pre><br />
It is functions from which our function kcbgtcr was executed. It is exactly parent function which you will see if will print call stack during the call of kcbgtcr.<br />
If you would print call stack you will see (I&#8217;ve just added ustack() in the script):<br />
<pre class="brush: plain;">
kcbgtcr(0xFFFFFD7FFFDFB0F0,0,742,0) [tsn: 4 rdba: 0x100060a (4/1546) obj: 79218 dobj: 79218] where: 742

              oracle`kcbgtcr
              oracle`ktecgshx+0x3f6
              oracle`kteinicnt1+0x1cf
              oracle`qertbFetch+0xc2a
              oracle`opifch2+0xaa2
              oracle`opifch+0x3a
              oracle`opiodr+0x433
              oracle`ttcpip+0x599
              oracle`opitsk+0x600
              oracle`opiino+0x675
              oracle`opiodr+0x433
              oracle`opidrv+0x32e
              oracle`sou2o+0x57
              oracle`opimai_real+0x219
              oracle`ssthrdmain+0x14e
              oracle`main+0xcb
              oracle`0x159e67c

kcbgtcr(0xFFFFFD7FFFDFAED0,0,743,0) [tsn: 4 rdba: 0x100060a (4/1546) obj: 79218 dobj: 79218] where: 743

              oracle`kcbgtcr
              oracle`ktecgshx+0x3f6
              oracle`kteinpscan+0x1b6
              oracle`kteiniscan+0x2c
              oracle`kdselini+0x2f
              oracle`kdsirs1+0x792
              oracle`kdsirs+0x2b
              oracle`qertbFetch+0xbb0
              oracle`opifch2+0xaa2
              oracle`opifch+0x3a
              oracle`opiodr+0x433
              oracle`ttcpip+0x599
              oracle`opitsk+0x600
              oracle`opiino+0x675
              oracle`opiodr+0x433
              oracle`opidrv+0x32e
              oracle`sou2o+0x57
              oracle`opimai_real+0x219
              oracle`ssthrdmain+0x14e
              oracle`main+0xcb

kcbgtcr(0xFFFFFD7FFDC3B2E0,0,860,0) [tsn: 4 rdba: 0x100060b (4/1547) obj: 79218 dobj: 79218] where: 860

              oracle`kcbgtcr
              oracle`ktrget2+0x27d
              oracle`kdstgr+0x46b
              oracle`qertbFetch+0x466
              oracle`opifch2+0xaa2
              oracle`opifch+0x3a
              oracle`opiodr+0x433
              oracle`ttcpip+0x599
              oracle`opitsk+0x600
              oracle`opiino+0x675
              oracle`opiodr+0x433
              oracle`opidrv+0x32e
              oracle`sou2o+0x57
              oracle`opimai_real+0x219
              oracle`ssthrdmain+0x14e
              oracle`main+0xcb
              oracle`0x159e67c
</pre></p>
<p>So, 3 consistent gets, 3 logical reads were performed.<br />
Two times block (4/1546) &#8211; this is segment header &#8211; has been read. The first time from function kteinicnt and the second time from function kteinpscan.<br />
And the third get is block (4/1547) &#8211; this is data block &#8211; was read from function kdstgr.</p>
<p>After that, if you push Ctrl+C, dtracelio script will be finished, and you will see Summary section:<br />
<pre class="brush: plain;">
^C

========================= Summary ==========================
object_id    data_object_id  function     where        count
79218        79218           kcbgtcr      742          1
79218        79218           kcbgtcr      743          1
79218        79218           kcbgtcr      860          1
</pre><br />
Summary section is aggregation of all calls grouped by object_id, data_object_id, function, where.</p>
<p><strong>example 2</strong><br />
There was question on <a href="http://www.sql.ru/forum/actualthread.aspx?bid=3&amp;tid=876321">sql.ru</a>. The author asked &#8220;why number of current gets (db block gets) during update in distributed transaction is more then in usual, non-distributed transaction?&#8221;.<br />
In his case it were:<br />
3070178 current gets in non-distributed transaction<br />
6070211 current gets in distributed transaction</p>
<p>So, let&#8217;s find out what exactly is read during distributed transaction and is not read in usual, non-distributed transaction.</p>
<p><pre class="brush: plain;">
SQL&gt; select * from v$version;
 
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
PL/SQL Release 11.2.0.2.0 - Production
CORE	11.2.0.2.0	Production
TNS for Solaris: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production

SQL&gt; @spid
 
       SID ORACLE_DEDICATED_PROCESS CLIENTPID
---------- ------------------------ ------------------------
         1 16727                    900:408

SQL&gt; create table test as select * from all_objects;
 
Table created

SQL&gt; alter session set optimizer_dynamic_sampling=0;
 
Session altered

SQL&gt; @10046
 
Session altered
</pre></p>
<p>Here we are executing dtracelio.d:<br />
<pre class="brush: plain;">
dtracelio.d 16727 0
</pre> Here I have asked to show me only summary section of executions of functions performing logical I/O by process 16727.</p>
<p>1st update, without distributed transaction<br />
<pre class="brush: plain;">
SQL&gt; begin
  2      update test set owner = owner;
  3      commit;
  4  end;
  5  /
 
PL/SQL procedure successfully completed
</pre></p>
<p>After the update has been completed we can push Ctrl+C to finish the DTrace and see the Summary section:<br />
<pre class="brush: plain;">
^C
========================= Summary ==========================
object_id    data_object_id  function     where        count
...
0            -1              kcbgcur      48           16
0            -1              kcbgcur      156          16
17           17              kcbgtcr      869          18
0            -1              kcbgcur      51           253
0            -1              kcbgcur      79           1365
79204        79204           kcbgcur      878          1656
0            -1              kcbgcur      50           2357
-1           79204           kcbgcur      859          2357
79204        79204           kcbgtcr      869          2726
79204        79204           kcbgcur      1053         75105
</pre></p>
<p>An excerpt from raw 10046 trace:<br />
<pre class="brush: plain;">
=====================
PARSING IN CURSOR #18446741324891442008 len=60 dep=0 uid=0 oct=47 lid=0 tim=229288260276 hv=2516922591 ad='88f557c0' sqlid='6sj578yb0ac6z'
begin
    update test set owner = owner;
    commit;
end;
END OF STMT
....
EXEC #18446741324891442008:c=2780000,e=4884618,p=0,cr=2776,cu=84545,mis=0,r=1,dep=0,og=1,plh=0,tim=229293144944
</pre><br />
Pay attention, cu=84545.</p>
<p>Now let&#8217;s execute the same update within distributed transaction<br />
<pre class="brush: plain;">
SQL&gt; declare
  2    i integer;
  3  begin
  4  
  5    select count(*) into i from dual@dblink;
  6    update test set owner = owner;
  7    commit;
  8  
  9  end;
 10  /
</pre></p>
<p><pre class="brush: plain;">
PARSING IN CURSOR #18446741324891442008 len=122 dep=0 uid=0 oct=47 lid=0 tim=229346672450 hv=1954107067 ad='88f04bc0' sqlid='arss8t9u7kmpv'
declare
  i integer;
begin
  select count(*) into i from dual@dblink;
  update test set owner = owner;
  commit;
end;
END OF STMT
...
EXEC #18446741324891442008:c=2950000,e=3432722,p=0,cr=2298,cu=159479,mis=0,r=1,dep=0,og=1,plh=0,tim=229350105247
</pre></p>
<p>As we can see cu=159479, about in two times more then in previous execution.</p>
<p>dtracelio.d output:<br />
<pre class="brush: plain;">
========================= Summary ==========================
object_id    data_object_id  function     where        count
...
0            -1              kcbgcur      48           16
0            -1              kcbgcur      156          16
17           17              kcbgtcr      869          18
0            -1              kcbgcur      51           49
0            -1              kcbgcur      50           442
-1           79204           kcbgcur      859          442
0            -1              kcbgcur      79           1161
79204        79204           kcbgcur      878          1177
79204        79204           kcbgtcr      869          2248
79204        79204           kcbgcur      1053         75105
0            -1              kcbgcur      109          79853
</pre></p>
<p>Pay attention on the last row<br />
<pre class="brush: plain;">
0            -1              kcbgcur      109          79853
</pre><br />
79853 additional db block gets of some undo block which does not exist in previous output.</p>
<p>If we take a look at the output of dtracelio.d with enabled output of each call we will see something like this:<br />
<pre class="brush: plain;">
...
kcbgcur(0xFFFFFD7FFDC42D68,2,1053,0) [tsn: 0 rdba: 0x4186e6 (1/100070) obj: 79204 dobj: 79204] where: 1053
kcbgcur(0xFFFFFD7FFFDF46A0,1,109,0) [tsn: 2 rdba: 0xc000f0 (3/240) obj: 0 dobj: -1] where: 109
kcbgcur(0xFFFFFD7FFDC42D68,2,1053,0) [tsn: 0 rdba: 0x4186e6 (1/100070) obj: 79204 dobj: 79204] where: 1053
kcbgcur(0xFFFFFD7FFFDF46A0,1,109,0) [tsn: 2 rdba: 0xc000f0 (3/240) obj: 0 dobj: -1] where: 109
kcbgcur(0xFFFFFD7FFDC42D68,2,1053,0) [tsn: 0 rdba: 0x4186e6 (1/100070) obj: 79204 dobj: 79204] where: 1053
kcbgcur(0xFFFFFD7FFFDF46A0,1,109,0) [tsn: 2 rdba: 0xc000f0 (3/240) obj: 0 dobj: -1] where: 109
kcbgcur(0xFFFFFD7FFDC42D68,2,1053,0) [tsn: 0 rdba: 0x4186e6 (1/100070) obj: 79204 dobj: 79204] where: 1053
kcbgcur(0xFFFFFD7FFFDF46A0,1,109,0) [tsn: 2 rdba: 0xc000f0 (3/240) obj: 0 dobj: -1] where: 109
kcbgcur(0xFFFFFD7FFDC42D68,2,1053,0) [tsn: 0 rdba: 0x4186e6 (1/100070) obj: 79204 dobj: 79204] where: 1053
kcbgcur(0xFFFFFD7FFFDF46A0,1,109,0) [tsn: 2 rdba: 0xc000f0 (3/240) obj: 0 dobj: -1] where: 109
kcbgcur(0xFFFFFD7FFDC42D68,2,1053,0) [tsn: 0 rdba: 0x4186e6 (1/100070) obj: 79204 dobj: 79204] where: 1053
...
</pre><br />
Here we can see these additional calls &#8220;where: 109&#8243; and that the same block (3/240) is read during these calls.<br />
Here is only excerpt, but I checked, all 79853 calls for the same block from the same &#8220;where&#8221; (109 in this case).<br />
<em>Update: updated version of <a href="http://alexanderanokhin.wordpress.com/scripts/dtracelio-d/">dtracelio.d</a> allows to see that these current gets were performed in shared mode, so content of the block is not changed.</em></p>
<p>Let&#8217;s try to look at inside the block.<br />
<pre class="brush: plain;">
alter system dump datafile 3 block 240;
</pre><br />
An excerpt from block dump:<br />
<pre class="brush: plain;">
...
Block dump from disk:
buffer tsn: 2 rdba: 0x00c000f0 (3/240)
scn: 0x0000.0015240d seq: 0x02 flg: 0x04 tail: 0x240d2602
frmt: 0x02 chkval: 0x54dc type: 0x26=KTU SMU HEADER BLOCK
...
</pre>So, this is the undo header.</p>
<p>What is &#8220;where: 109&#8243;? &#8220;Where&#8221; means location in code, let&#8217;s try to find function name with indx 109<br />
<pre class="brush: plain;">
SQL&gt; select indx, kcbwhdes from x$kcbwh where indx = 109;
 
      INDX KCBWHDES
---------- ----------------------------------------------------------------
       109 ktuwh87: ktugus:ktuGetExtTxnInfo
</pre><br />
It is exactly what you will see if print call stack during call with &#8220;where: 109&#8243; (the second row, function from kcbgcur was called is ktuGetExtTxnInfo):<br />
<pre class="brush: plain;"> 
 0000000002c63d01 kcbgcur () + 1
 00000000029272d4 ktuGetExtTxnInfo () + 174
 00000000028f5193 ktugti () + 13
 00000000028e2561 ktuchg2 () + 1971
 0000000002944d2f ktbchg2 () + 12f
 0000000001b04809 kdu_array_flush_retry () + c69
 0000000001b01f42 kdu_array_buf () + 522
 0000000001af5390 kduurp () + 460
 0000000001adea00 kdusru () + 13e0
 0000000001ac6c12 kauupd () + 1b2
 0000000005a3370c updrow () + a5c
 0000000007b8bd27 qerupFetch () + 397
 000000000367cfdc qerstFetch () + 58c
 0000000005a3f2fe updaul () + 4ce
 0000000005a4469d updThreePhaseExe () + 1cd
 0000000005a43abd updexe () + 1ed
 000000000447b2c5 opiexe () + 2455
...
</pre></p>
<p>It were for 11.2.0.2. In my 10.2.0.5 all these additional calls are with &#8220;where: 383&#8243;, &#8220;ktuwh02: ktugus&#8221; which Jonathan Lewis called &#8220;Get Undo Segment header for commit&#8221; (Seems it is not correct descriptoin).<br />
And following call stack is during these calls<br />
<pre class="brush: plain;">
              oracle`kcbgcur
              oracle`ktugusc+0x321
              oracle`ktugti+0xf2
              oracle`ktuchg+0x139a
              oracle`ktbchg2+0x115
              oracle`kddchg+0x2c2
              oracle`kddlok+0x7da
              oracle`kddlkr+0x16a
              oracle`updrow+0x2417
              oracle`qerupRowProcedure+0x4f
              oracle`qerupFetch+0x339
              oracle`updaul+0x481
              oracle`updThreePhaseExe+0xc72
              oracle`updexe+0x171
              oracle`opiexe+0xf1b
              oracle`opipls+0x98e
              oracle`opiodr+0x433
              oracle`rpidrus+0xde
              oracle`skgmstack+0x80
              oracle`rpidru+0x86
</pre></p>
<p><Strong>Conclusion</strong><br />
We have determined what exactly are extra current gets within distributed transaction.<br />
Why Oracle 79853 times reads undo header is a topic for further research. I would prefer to write another post about it.</p>
<p><Strong>Little oftopic</strong>, but interesting point is that Oracle opens distributed transaction (binds undo header, transaction appears in v$transaction, v$global_transaction) for PLSQL block with dblinks during hard parsing. If hard parsing is not performed then during parsing/execution of statement with dblink.<br />
It follows that if you will execute a statement like this<br />
<pre class="brush: plain;">
declare
    i integer;
begin
    DML;
    DML;
    DML;
    if 1=0 then
        select count(*) into i from dual@dblink;
    end if;
end;
</pre><br />
then either it will be executed within dustributed transaction (with extra current gets) or not depends on will hard parsing be performed or not.</p>
<p>p.s.<br />
Attention!<br />
I am still looking for additional information about<br />
* structure kcbds,<br />
* argments of functions kcbgtcr<br />
* argments of functions kcbgcur<br />
If you have this information please let me know.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/alexanderanokhin.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/alexanderanokhin.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/alexanderanokhin.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/alexanderanokhin.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/alexanderanokhin.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/alexanderanokhin.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/alexanderanokhin.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/alexanderanokhin.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/alexanderanokhin.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/alexanderanokhin.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/alexanderanokhin.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/alexanderanokhin.wordpress.com/224/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/alexanderanokhin.wordpress.com/224/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/alexanderanokhin.wordpress.com/224/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=224&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://alexanderanokhin.wordpress.com/2011/11/13/dynamic-tracing-of-oracle-logical-io/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/48a7cd21f3eee64ace66d6668a3e1223?s=96&#38;d=http%3A%2F%2Fs0.wp.com%2Fi%2Fmu.gif&#38;r=G" medium="image">
			<media:title type="html">alexanderanokhin</media:title>
		</media:content>
	</item>
		<item>
		<title>row_number &lt; n</title>
		<link>http://alexanderanokhin.wordpress.com/2011/07/10/row_number-n/</link>
		<comments>http://alexanderanokhin.wordpress.com/2011/07/10/row_number-n/#comments</comments>
		<pubDate>Sun, 10 Jul 2011 17:31:53 +0000</pubDate>
		<dc:creator>Alexander Anokhin</dc:creator>
				<category><![CDATA[Oracle]]></category>

		<guid isPermaLink="false">http://alexanderanokhin.wordpress.com/?p=182</guid>
		<description><![CDATA[It&#8217;s about feature of WINDOW NOSORT STOPKEY and difference between row_number() = 1 and row_number() &#60; 2. Update 06.12.2011 Note: The following effects appear only with Index Full Scan and does not exist with Index Range Scan. This post related with a question on sql.ru which I answered a coulpe days ago. Preparing of data: [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=182&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s about feature of WINDOW NOSORT STOPKEY and difference between row_number() = 1 and row_number() &lt; 2.<span id="more-182"></span></p>
<p><em><strong>Update 06.12.2011</strong><br />
Note: The following effects appear only with Index Full Scan and does not exist with Index Range Scan.</em></p>
<p>This post related with a question on <a title="sql.ru" href="http://www.sql.ru/forum/actualthread.aspx?bid=3&amp;tid=863816">sql.ru</a> which I answered a coulpe days ago.</p>
<p>Preparing of data:</p>
<p><pre class="brush: plain;">
SQL&gt; create table test (id not null, val) pctfree 99
  2    as select level, level
  3         from dual
  4      connect by level &lt;= 1000;

Table created

SQL&gt; create index test_idx on test(id);

Index created
</pre></p>
<p>First, I try to use row_number() = 1</p>
<p><pre class="brush: plain;">
SQL&gt; select id, val
  2    from (select --+ index(t test_idx)
  3                 id,
  4                 val,
  5                 row_number() over(order by id) as rn
  6            from test t)
  7   where rn = 1;

        ID        VAL
---------- ----------
         1          1
</pre></p>
<p>runtime execution plan of the query:</p>
<p><pre class="brush: plain;">
-------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Starts | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |          |      1 |      1 |00:00:00.01 |       3 |
|*  1 |  VIEW                         |          |      1 |      1 |00:00:00.01 |       3 |
|*  2 |   WINDOW NOSORT STOPKEY       |          |      1 |      1 |00:00:00.01 |       3 |
|   3 |    TABLE ACCESS BY INDEX ROWID| TEST     |      1 |      2 |00:00:00.01 |       3 |
|   4 |     INDEX FULL SCAN           | TEST_IDX |      1 |      2 |00:00:00.01 |       2 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(&quot;RN&quot;=1)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY &quot;ID&quot;)&lt;=1)
</pre></p>
<p>As we can see, step 2 WINDOW NOSORT STOPKEY returns 1 row to parent rowsource after getting of 2 rows from child rowsource.</p>
<p>Now compare this with construction &#8220;row_number() &lt; 2&#8243; which some developers prefer to use.</p>
<p><pre class="brush: plain;">
SQL&gt; select id, val
  2    from (select --+ index(t test_idx)
  3                 id,
  4                 val,
  5                 row_number() over(order by id) as rn
  6            from test t)
  7   where rn &lt; 2;

        ID        VAL
---------- ----------
         1          1
</pre></p>
<p>And runtime execution plan of the query:</p>
<p><pre class="brush: plain;">
-------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Starts | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |          |      1 |      1 |00:00:00.01 |       3 |
|*  1 |  VIEW                         |          |      1 |      1 |00:00:00.01 |       3 |
|*  2 |   WINDOW NOSORT STOPKEY       |          |      1 |      2 |00:00:00.01 |       3 |
|   3 |    TABLE ACCESS BY INDEX ROWID| TEST     |      1 |      3 |00:00:00.01 |       3 |
|   4 |     INDEX FULL SCAN           | TEST_IDX |      1 |      3 |00:00:00.01 |       2 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(&quot;RN&quot;&lt;2)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY &quot;ID&quot;)&lt;2)
</pre></p>
<p>In this case step 2 &#8220;WINDOW NOSORT STOPKEY&#8221; returns 2 rows to parent rowsource after getting of 3 rows from child rowsource.</p>
<p>Obviously in some cases this little difference (just one row) can lead to the large difference in the amount of work and, consequently, possible performance degradation of a query.<br />
One example provided in thread on sql.ru. Child rowsource is &#8220;GROUP BY NOSORT&#8221; which needs to read N additional rows from an index and a table to return the third grouped value.<br />
Another example (below) shows case where for return the third row Oracle should read and filter a lot of rows thanks to filter predicates.</p>
<p><pre class="brush: plain;">
SQL&gt; select id, val
  2    from (select --+ index(t test_idx)
  3                 id,
  4                 val,
  5                 row_number() over(order by id) as rn
  6            from test t
  7           where val &lt;= 2 or val &gt; 900)
  8   where rn &lt; 2;

        ID        VAL
---------- ----------
         1          1
</pre></p>
<p>&nbsp;</p>
<p><pre class="brush: plain;">
-------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Starts | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |          |      1 |      1 |00:00:00.01 |     180 |
|*  1 |  VIEW                         |          |      1 |      1 |00:00:00.01 |     180 |
|*  2 |   WINDOW NOSORT STOPKEY       |          |      1 |      2 |00:00:00.01 |     180 |
|*  3 |    TABLE ACCESS BY INDEX ROWID| TEST     |      1 |      3 |00:00:00.01 |     180 |
|   4 |     INDEX FULL SCAN           | TEST_IDX |      1 |    901 |00:00:00.01 |       3 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(&quot;RN&quot;&lt;2)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY &quot;ID&quot;)&lt;2)
   3 - filter((&quot;VAL&quot;&lt;=2 OR &quot;VAL&quot;&gt;900))
</pre></p>
<p>Pay attention, 901 rows were fetched from the table, 3 + 180 logical reads were done, to check predicate &#8220;filter((&#8220;VAL&#8221;&lt;=2 OR &#8220;VAL&#8221;&gt;900))&#8221; and return the third row.</p>
<p>Compare the same query with rn = 1 instead of rn &lt; 2<br />
<pre class="brush: plain;">
SQL&gt; select id, val
  2    from (select --+ index(t test_idx)
  3                 id,
  4                 val,
  5                 row_number() over(order by id) as rn
  6            from test t
  7           where val &lt;= 2 or val &gt; 900)
  8   where rn = 1;

        ID        VAL
---------- ----------
         1          1
</pre></p>
<p><pre class="brush: plain;">
-------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Starts | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------
|*  1 |  VIEW                         |          |      1 |      1 |00:00:00.01 |       3 |
|*  2 |   WINDOW NOSORT STOPKEY       |          |      1 |      1 |00:00:00.01 |       3 |
|*  3 |    TABLE ACCESS BY INDEX ROWID| TEST     |      1 |      2 |00:00:00.01 |       3 |
|   4 |     INDEX FULL SCAN           | TEST_IDX |      1 |      2 |00:00:00.01 |       2 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(&quot;RN&quot;=1)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY &quot;ID&quot;)&lt;=1)
   3 - filter((&quot;VAL&quot;&lt;=2 OR &quot;VAL&quot;&gt;900))
</pre><br />
Only 2 rows were fetched from index TEST_IDX and only 3 logical reads only were done.</p>
<p>p.s.<br />
By the way, usual rownum and COUNT STOPKEY read only 1 row from child rowsource in both cases.</p>
<p><pre class="brush: plain;">
SQL&gt; select id, val
  2    from (select --+ index(t test_idx)
  3                 id,
  4                 val
  5            from test t
  6           where val &lt;= 2 or val &gt; 900)
  7   where rownum &lt; 2;

        ID          N
---------- ----------
         1          1
</pre><br />
<pre class="brush: plain;">
------------------------------------------------------------------------------------------
| Id  | Operation                    | Name     | Starts | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |      1 |      1 |00:00:00.01 |       3 |
|*  1 |  COUNT STOPKEY               |          |      1 |      1 |00:00:00.01 |       3 |
|*  2 |   TABLE ACCESS BY INDEX ROWID| TEST     |      1 |      1 |00:00:00.01 |       3 |
|   3 |    INDEX FULL SCAN           | TEST_IDX |      1 |      1 |00:00:00.01 |       2 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(ROWNUM&lt;2)
   2 - filter((&quot;VAL&quot;&lt;=2 OR &quot;VAL&quot;&gt;900))
</pre></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/alexanderanokhin.wordpress.com/182/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/alexanderanokhin.wordpress.com/182/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/alexanderanokhin.wordpress.com/182/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/alexanderanokhin.wordpress.com/182/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/alexanderanokhin.wordpress.com/182/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/alexanderanokhin.wordpress.com/182/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/alexanderanokhin.wordpress.com/182/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/alexanderanokhin.wordpress.com/182/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/alexanderanokhin.wordpress.com/182/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/alexanderanokhin.wordpress.com/182/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/alexanderanokhin.wordpress.com/182/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/alexanderanokhin.wordpress.com/182/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/alexanderanokhin.wordpress.com/182/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/alexanderanokhin.wordpress.com/182/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=182&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://alexanderanokhin.wordpress.com/2011/07/10/row_number-n/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/48a7cd21f3eee64ace66d6668a3e1223?s=96&#38;d=http%3A%2F%2Fs0.wp.com%2Fi%2Fmu.gif&#38;r=G" medium="image">
			<media:title type="html">alexanderanokhin</media:title>
		</media:content>
	</item>
		<item>
		<title>Evolution of INDEX RANGE/FULL SCAN (MIN/MAX)</title>
		<link>http://alexanderanokhin.wordpress.com/2011/06/23/evolution-of-index-rangefull-scan-minmax/</link>
		<comments>http://alexanderanokhin.wordpress.com/2011/06/23/evolution-of-index-rangefull-scan-minmax/#comments</comments>
		<pubDate>Thu, 23 Jun 2011 17:15:55 +0000</pubDate>
		<dc:creator>Alexander Anokhin</dc:creator>
				<category><![CDATA[Oracle]]></category>

		<guid isPermaLink="false">http://alexanderanokhin.wordpress.com/?p=168</guid>
		<description><![CDATA[This post is about some little difference in CBO estimations of INDEX FULL SCAN (MIN/MAX) between versions 10.2, 11.1 and 11.2. Script for the further tests: Note: Pay attention that column object_name which we will be used for max() is nullable. It is for demonstrate bug in costing in 10.2. Another details are the same [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=168&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>This post is about some little difference in CBO estimations of INDEX FULL SCAN (MIN/MAX) between versions 10.2, 11.1 and 11.2.<br />
<span id="more-168"></span><br />
Script for the further tests:<br />
<em>Note: Pay attention that column object_name which we will be used for max() is nullable. It is for demonstrate bug in costing in 10.2. Another details are the same for nullable and not nullable column.</em></p>
<p><pre class="brush: plain;">
create table test as select * from all_objects;
create index idx on test(object_name);
alter table test modify(object_name null);
exec dbms_stats.gather_table_stats(user, 'test', estimate_percent=&gt;100, cascade =&gt; true);
explain plan for select max(object_name) from test;
select * from table(dbms_xplan.display);
</pre></p>
<p><strong>10.2.0.4</strong><br />
<pre class="brush: plain;">
SQL&gt; create table test as select * from all_objects;

Table created

SQL&gt; create index idx on test(object_name);

Index created

SQL&gt; alter table test modify(object_name null);

Table altered

SQL&gt; exec dbms_stats.gather_table_stats(user, 'test', estimate_percent=&gt;100, cascade =&gt; true);

PL/SQL procedure successfully completed

SQL&gt; explain plan for select max(object_name) from test;

Explained

select * from table(dbms_xplan.display);

-----------------------------------------------------------------------------------
| Id  | Operation                  | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |      |     1 |    24 |   460  (15)| 00:00:01 |
|   1 |  SORT AGGREGATE            |      |     1 |    24 |            |          |
|   2 |   INDEX FULL SCAN (MIN/MAX)| IDX  | 53773 |  1260K|            |          |
-----------------------------------------------------------------------------------
</pre></p>
<p>Notice, that cardinality of step 2 = 53773 (should be = 1 obviously) and more important(!) cost = 460.<br />
Real cost of this operation is much less. Access path INDEX FULL SCAN (MIN/MAX) means that only leftmost or rightmost index block will be read. Thus cost of this operation should be equal blevel+1.<br />
This problem is investigated below.</p>
<p><strong>11.1.0.6</strong><br />
<pre class="brush: plain;">
-----------------------------------------------------------------------------------
| Id  | Operation                  | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |      |     1 |    25 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE            |      |     1 |    25 |            |          |
|   2 |   INDEX FULL SCAN (MIN/MAX)| IDX  | 65965 |  1610K|     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------
</pre></p>
<p>Notice, that the cost is correct. However cardinality of step 2 is still not correct. It should not affect anything becasuse cardinality of parent step SORT AGGREGATE or FIRST ROW is 1.</p>
<p><strong>11.2.0.2</strong><br />
<pre class="brush: plain;">
-----------------------------------------------------------------------------------
| Id  | Operation                  | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |      |     1 |    11 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE            |      |     1 |    11 |            |          |
|   2 |   INDEX FULL SCAN (MIN/MAX)| IDX  |     1 |    11 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------
</pre><br />
Notice, that the cost and the cardinality are correct.</p>
<p><strong>10.2.0.4 more details</strong><br />
Let&#8217;s return to 10.2.0.4. In this case such high cost is a real problem.<br />
Execution plan with runtime statistics confirms this</p>
<p><pre class="brush: plain;">
------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |
------------------------------------------------------------------------------------------------------
|   1 |  SORT AGGREGATE            |      |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |
|   2 |   INDEX FULL SCAN (MIN/MAX)| IDX  |      1 |  53773 |      1 |00:00:00.01 |       3 |      1 |
------------------------------------------------------------------------------------------------------
Note
-----
   - rule based optimizer used (consider using cbo)
</pre></p>
<p>The problem is that if there will exist another access path with cost less than 460 (in our case) Oracle chooses that cheaper access path instead of MIN/MAX optimization which is the best way obviously.</p>
<p>For example, let&#8217;s create another index containing column object_name:<br />
<pre class="brush: plain;">
SQL&gt; create index idx2 on test(owner, object_name);

Index created

SQL&gt; explain plan for select max(object_name) from test;

Explained

SQL&gt; @plan

------------------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |     1 |    24 |   199  (20)| 00:00:01 |
|   1 |  SORT AGGREGATE       |      |     1 |    24 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX2 | 53773 |  1260K|   199  (20)| 00:00:01 |
------------------------------------------------------------------------------
</pre></p>
<p>Workaround is in addition &#8220;is not null&#8221; condition.</p>
<p><pre class="brush: plain;">
SQL&gt; explain plan for select max(object_name) from test where object_name is not null;

Explained

SQL&gt; @plan
--------------------------------------------------------------------------------
Plan hash value: 1520840685
------------------------------------------------------------------------------------
| Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |     1 |    24 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE             |      |     1 |    24 |            |          |
|   2 |   FIRST ROW                 |      | 53773 |  1260K|     3   (0)| 00:00:01 |
|   3 |    INDEX FULL SCAN (MIN/MAX)| IDX  | 53773 |  1260K|     3   (0)| 00:00:01 |
------------------------------------------------------------------------------------
</pre><br />
So, cost is correct now (notice that cardinality is still be crazy).</p>
<p>But how and why this cost was calculated?<br />
Below is an excerpt from 10053 trace</p>
<p><pre class="brush: plain;">
BASE STATISTICAL INFORMATION
***********************
Table Stats::
  Table: TEST  Alias: TEST
    #Rows: 53773  #Blks:  1571  AvgRowLen:  97.00
Index Stats::
  Index: IDX  Col#: 2
    LVLS: 2  #LB: 538  #DK: 29266  LB/K: 1.00  DB/K: 1.00  CLUF: 27735.00
***************************************
SINGLE TABLE ACCESS PATH
  -----------------------------------------
  BEGIN Single Table Cardinality Estimation
  -----------------------------------------
  Table: TEST  Alias: TEST
    Card: Original: 53773  Rounded: 53773  Computed: 53773.00  Non Adjusted: 53773.00
  -----------------------------------------
  END   Single Table Cardinality Estimation
  -----------------------------------------
  Access Path: TableScan
    Cost:  459.51  Resp: 459.51  Degree: 0
      Cost_io: 392.00  Cost_cpu: 18270051
      Resp_io: 392.00  Resp_cpu: 18270051
  Best:: AccessPath: TableScan
         Cost: 459.51  Degree: 1  Resp: 459.51  Card: 53773.00  Bytes: 0
***************************************
OPTIMIZER STATISTICS AND COMPUTATIONS
***************************************
GENERAL PLANS
***************************************
Considering cardinality-based initial join order.
Permutations for Starting Table :0
***********************
Join order[1]:  TEST[TEST]#0
***********************
Best so far: Table#: 0  cost: 459.5053  card: 53773.0000  bytes: 1290552
(newjo-stop-1) k:0, spcnt:0, perm:1, maxperm:80000
*********************************
Number of join permutations tried: 1
*********************************
Final - All Rows Plan:  Best join order: 1
  Cost: 459.5053  Degree: 1  Card: 53773.0000  Bytes: 1290552
  Resc: 459.5053  Resc_io: 392.0000  Resc_cpu: 18270051
  Resp: 459.5053  Resp_io: 392.0000  Resc_cpu: 18270051
kkoipt: Query block SEL$1 (#0)
******* UNPARSED QUERY IS *******
SELECT MAX(&quot;TEST&quot;.&quot;OBJECT_NAME&quot;) &quot;MAX(OBJECT_NAME)&quot; FROM &quot;SA&quot;.&quot;TEST&quot; &quot;TEST&quot;
kkoqbc-subheap (delete addr=ffffffff7cf18940, in-use=11424, alloc=11872)
kkoqbc-end
          : call(in-use=7936, alloc=32712), compile(in-use=35976, alloc=36536)
apadrv-end: call(in-use=7936, alloc=32712), compile(in-use=36760, alloc=40552)

sql_id=1rq6zdmdax2c0.
Current SQL statement for this session:
explain plan for select max(object_name) from test

============
Plan Table
============
----------------------------------------------+-----------------------------------+
| Id  | Operation                   | Name    | Rows  | Bytes | Cost  | Time      |
----------------------------------------------+-----------------------------------+
| 0   | SELECT STATEMENT            |         |       |       |   460 |           |
| 1   |  SORT AGGREGATE             |         |     1 |    24 |       |           |
| 2   |   INDEX FULL SCAN (MIN/MAX) | IDX     |   53K | 1260K |       |           |
----------------------------------------------+-----------------------------------+
Predicate Information:
----------------------
</pre></p>
<p>We can see that 460 is the cost of full table scan. And declarated best access path is also TableScan. However as a result access path is INDEX FULL SCAN (MIN/MAX).</p>
<p>I suggest that hint is in &#8220;notes&#8221; section of runtime execution plan:<br />
<pre class="brush: plain;">
---------------------------------------------------------------------------------------------
| Id  | Operation                  | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------
|   1 |  SORT AGGREGATE            |      |      1 |      1 |      1 |00:00:00.01 |       3 |
|   2 |   INDEX FULL SCAN (MIN/MAX)| IDX  |      1 |  53773 |      1 |00:00:00.01 |       3 |
---------------------------------------------------------------------------------------------
Note
-----
   - rule based optimizer used (consider using cbo)
</pre></p>
<p>Pay attention &#8220;rule based optimizer used&#8221;. Looks like the bug 8467123 or 8323407.<br />
Seems some exception is caused during CBO optimization and Oracle switches to RBO although cost also present in &#8220;explained&#8221; plan.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/alexanderanokhin.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/alexanderanokhin.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/alexanderanokhin.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/alexanderanokhin.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/alexanderanokhin.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/alexanderanokhin.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/alexanderanokhin.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/alexanderanokhin.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/alexanderanokhin.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/alexanderanokhin.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/alexanderanokhin.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/alexanderanokhin.wordpress.com/168/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/alexanderanokhin.wordpress.com/168/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/alexanderanokhin.wordpress.com/168/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=168&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://alexanderanokhin.wordpress.com/2011/06/23/evolution-of-index-rangefull-scan-minmax/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/48a7cd21f3eee64ace66d6668a3e1223?s=96&#38;d=http%3A%2F%2Fs0.wp.com%2Fi%2Fmu.gif&#38;r=G" medium="image">
			<media:title type="html">alexanderanokhin</media:title>
		</media:content>
	</item>
		<item>
		<title>Non-determinisitic PLSQL function and Index Range Scan</title>
		<link>http://alexanderanokhin.wordpress.com/2011/03/27/non-determinisitic-plsql-function-and-index-range-scan/</link>
		<comments>http://alexanderanokhin.wordpress.com/2011/03/27/non-determinisitic-plsql-function-and-index-range-scan/#comments</comments>
		<pubDate>Sun, 27 Mar 2011 06:56:06 +0000</pubDate>
		<dc:creator>Alexander Anokhin</dc:creator>
				<category><![CDATA[Oracle]]></category>

		<guid isPermaLink="false">http://alexanderanokhin.wordpress.com/?p=129</guid>
		<description><![CDATA[Yesterday I met a funny issue. Checked in versions 10.2.0.4, 10.2.0.5 &#8211; reproduced. 11.1.0.7 &#8211; not (partially, see update below. For IOT reproduced in 11.1, 11.2). Thus, this bug could be considered as not actual, but may shed light on internals of IRS. Let&#8217;s create a table with 100 rows, n = 1..100 and simple [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=129&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Yesterday I met a funny issue.</p>
<p>Checked in versions 10.2.0.4, 10.2.0.5 &#8211; reproduced. 11.1.0.7 &#8211; not (partially, see update below. For IOT reproduced in 11.1, 11.2).<br />
Thus, this bug could be considered as not actual, but may shed light on internals of IRS.<br />
<span id="more-129"></span></p>
<p>Let&#8217;s create a table with 100 rows, n = 1..100 and simple non-deterministic function.</p>
<p><pre class="brush: plain;">
SQL&gt; create table test as select rownum n,
  2                              rownum m,
  3                              'n# ' || rownum rown
  4                         from dual
  5                      connect by level &lt;= 100 
  6  / 
Table created 

SQL&gt; create index idx on test(n,m) -- not unique index
  2  /

Index created

SQL&gt; create or replace function f  return integer
  2  as
  3    n integer;
  4  begin
  5    n := dbms_random.VALUE(1, 100);
  6    dbms_output.put_line('function f called. result = ' || n);
  7    return n;
  8  end;
  9  /

Function created
</pre></p>
<p>How many times function f will be executed in the query below?</p>
<p><pre class="brush: plain;">
SQL&gt; explain plan for
  2  select n,
  3         m,
  4         rown
  5    from test
  6   where n = f
  7  /

Explained

SQL&gt; select * from table(dbms_xplan.display(null,null,'basic +predicate'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 4092295894
--------------------------------------------
| Id  | Operation                   | Name |
--------------------------------------------
|   0 | SELECT STATEMENT            |      |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST |
|*  2 |   INDEX RANGE SCAN          | IDX  |
--------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(&quot;N&quot;=&quot;F&quot;())
</pre></p>
<p>Let&#8217;s check it.</p>
<p><pre class="brush: plain;">
SQL&gt; begin
  2    dbms_random.seed(2); -- 2 is just a suitable number to reproduce tests
  3  end;
  4  /

PL/SQL procedure successfully completed

SQL&gt; select n,
  2         m,
  3         rown
  4    from test
  5   where n = f;

         N          M ROWN
---------- ---------- -------------------------------------------

function f called. result = 10
function f called. result = 68
</pre></p>
<p>Function f was executed twice and select has not returned anything.</p>
<p>One more attempt:</p>
<p><pre class="brush: plain;">
SQL&gt; select n,
  2         m,
  3         rown
  4    from test
  5   where n = f;

         N          M ROWN
---------- ---------- -------------------------------------------
         9          9 n# 9
         9         10 n# 10
         9         11 n# 11
         9         12 n# 12
         9         13 n# 13

function f called. result = 13
function f called. result = 9
</pre></p>
<p>The function has been executed twice again and five rows have been returned.<br />
Seems Oracle executes function the first time to determine the right border (13 in our case) of range and the second for the left border (9 in our case). As if &#8220;equal&#8221; would converted in &#8220;between&#8221;.<br />
Returned values (except of column N) correspond with range. Column N which has exactly been in condition with function is the same as the last execution of function.</p>
<p><strong>UPDATE 27.03.2011 </strong><br />
Looks like the problem has been fixed in 11g for normal indexes, but still exists for IOT in particular.<br />
executed in 11.1.0.7 and 11.2.0.2:<br />
<pre class="brush: plain;">
SQL&gt; create table test_iot (n,m,rown, primary key(n,m)) organization index
  2                     as select rownum n,
  3                               rownum m,
  4                               'n# ' || rownum rown
  5                          from dual
  6                       connect by level &lt;= 100
  7  /
 
Table created

SQL&gt; begin
  2    dbms_random.seed(2); -- just a suitable number to reproduce tests
  3  end;
  4  /
 
PL/SQL procedure successfully completed

SQL&gt; select n,
  2         m,
  3         rown
  4    from test_iot
  5   where n = f;
 
         N          M ROWN
---------- ---------- -------------------------------------------
 
function f called. result = 10
function f called. result = 68

SQL&gt; select n,
  2         m,
  3         rown
  4    from test_iot
  5   where n = f;
 
         N          M ROWN
---------- ---------- -------------------------------------------
         9          9 n# 9
        10         10 n# 10
        11         11 n# 11
        12         12 n# 12
        13         13 n# 13
 
function f called. result = 13
function f called. result = 9
</pre><br />
As we can see the similar result as example above. However, one detail has been changed: column N equal value from range.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/alexanderanokhin.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/alexanderanokhin.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/alexanderanokhin.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/alexanderanokhin.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/alexanderanokhin.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/alexanderanokhin.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/alexanderanokhin.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/alexanderanokhin.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/alexanderanokhin.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/alexanderanokhin.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/alexanderanokhin.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/alexanderanokhin.wordpress.com/129/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/alexanderanokhin.wordpress.com/129/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/alexanderanokhin.wordpress.com/129/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=129&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://alexanderanokhin.wordpress.com/2011/03/27/non-determinisitic-plsql-function-and-index-range-scan/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/48a7cd21f3eee64ace66d6668a3e1223?s=96&#38;d=http%3A%2F%2Fs0.wp.com%2Fi%2Fmu.gif&#38;r=G" medium="image">
			<media:title type="html">alexanderanokhin</media:title>
		</media:content>
	</item>
		<item>
		<title>SQL_TRACE event in 11g</title>
		<link>http://alexanderanokhin.wordpress.com/2011/02/26/sql_trace-event-in-11g/</link>
		<comments>http://alexanderanokhin.wordpress.com/2011/02/26/sql_trace-event-in-11g/#comments</comments>
		<pubDate>Sat, 26 Feb 2011 07:12:05 +0000</pubDate>
		<dc:creator>Alexander Anokhin</dc:creator>
				<category><![CDATA[Oracle]]></category>

		<guid isPermaLink="false">http://alexanderanokhin.wordpress.com/?p=107</guid>
		<description><![CDATA[There is one new excellent and powerful diagnostic event in 11g. Which is still not sufficiently known and popular. Just a couple links with good explanation of this event from Miladin Modrakovic and Tanel Poder. http://oraclue.com/2009/03/24/oracle-event-sql_trace-in-11g/ http://blog.tanelpoder.com/2010/06/23/the-full-power-of-oracles-diagnostic-events-part-2-oradebug-doc-and-11g-improvements<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=107&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>There is one new excellent and powerful diagnostic event in 11g. Which is still not sufficiently known and popular.</p>
<p>Just a couple links with good explanation of this event from Miladin Modrakovic and Tanel Poder.<br />
<a href="http://oraclue.com/2009/03/24/oracle-event-sql_trace-in-11g/">http://oraclue.com/2009/03/24/oracle-event-sql_trace-in-11g/</a><br />
<a href="http://blog.tanelpoder.com/2010/06/23/the-full-power-of-oracles-diagnostic-events-part-2-oradebug-doc-and-11g-improvements/">http://blog.tanelpoder.com/2010/06/23/the-full-power-of-oracles-diagnostic-events-part-2-oradebug-doc-and-11g-improvements</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/alexanderanokhin.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/alexanderanokhin.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/alexanderanokhin.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/alexanderanokhin.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/alexanderanokhin.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/alexanderanokhin.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/alexanderanokhin.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/alexanderanokhin.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/alexanderanokhin.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/alexanderanokhin.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/alexanderanokhin.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/alexanderanokhin.wordpress.com/107/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/alexanderanokhin.wordpress.com/107/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/alexanderanokhin.wordpress.com/107/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=107&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://alexanderanokhin.wordpress.com/2011/02/26/sql_trace-event-in-11g/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/48a7cd21f3eee64ace66d6668a3e1223?s=96&#38;d=http%3A%2F%2Fs0.wp.com%2Fi%2Fmu.gif&#38;r=G" medium="image">
			<media:title type="html">alexanderanokhin</media:title>
		</media:content>
	</item>
		<item>
		<title>When indexes degrade</title>
		<link>http://alexanderanokhin.wordpress.com/2011/02/22/when-indexes-degrade/</link>
		<comments>http://alexanderanokhin.wordpress.com/2011/02/22/when-indexes-degrade/#comments</comments>
		<pubDate>Tue, 22 Feb 2011 14:44:12 +0000</pubDate>
		<dc:creator>Alexander Anokhin</dc:creator>
				<category><![CDATA[Oracle]]></category>

		<guid isPermaLink="false">http://alexanderanokhin.wordpress.com/?p=62</guid>
		<description><![CDATA[Is there needed to regular rebuild of indexes? Sometimes it is necesarry. The reason is that during deletion from a table index leaf rows are not deleted from index, but only marked as deleted. Below is a simple example. Pay attention, 348 logical reads during index range scan which does not found any rows. This [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=62&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Is there needed to regular rebuild of indexes?<br />
Sometimes it is necesarry.<span id="more-62"></span> The reason is that during deletion from a table index leaf rows are not deleted from index, but only marked as deleted.</p>
<p>Below is a simple example.</p>
<p><pre class="brush: plain;">
SQL&gt; create table test as select * from all_objects where 1=0;

Table created

Executed in 0,344 seconds

SQL&gt; create index idx on test(owner, object_name);

Index created

SQL&gt; insert into test select * from all_objects;

53779 rows inserted

SQL&gt; delete from test;

53779 rows deleted

SQL&gt; commit;

Commit complete

&gt; select * from test where owner='SYS';
no rows selected

------------------------------------------------------------------------
| Id  | Operation                   | Name | Starts | A-Rows | Buffers |
------------------------------------------------------------------------
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST |      1 |      0 |     348 |
|*  2 |   INDEX RANGE SCAN          | IDX  |      1 |      0 |     348 |
------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(&quot;OWNER&quot;='SYS')
</pre></p>
<p><strong>Pay attention, 348 logical reads during index range scan which does not found any rows.</strong><br />
This especially actual for the tables whose lifecycle consists regular inserting and deleting of rows. E.g. the AQ tables.</p>
<p>&nbsp;</p>
<p>One of option to determine how many deleted entries consists an index is to use the statement:</p>
<p><pre class="brush: plain;">
analyze index  validate structure;
select lf_rows, del_lf_rows from index_stats;
</pre></p>
<p>In our example</p>
<p><pre class="brush: plain;">
SQL&gt; analyze index idx validate structure;

Index analyzed

SQL&gt; select lf_rows, del_lf_rows from index_stats;

   LF_ROWS DEL_LF_ROWS
---------- -----------
     40599       40599
</pre></p>
<p>Thus, our index consists of 40599 leaf rows and all of them are deleted.</p>
<p>&nbsp;</p>
<p>Let try to rebuild index and execute the same query</p>
<p><pre class="brush: plain;">

&gt; select * from test where owner='SYS';

no rows selected

------------------------------------------------------------------------
| Id  | Operation                   | Name | Starts | A-Rows | Buffers |
------------------------------------------------------------------------
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST |      1 |      0 |       1 |
|*  2 |   INDEX RANGE SCAN          | IDX  |      1 |      0 |       1 |
------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(&quot;OWNER&quot;='SYS')
</pre></p>
<p>Now only 1 logical read.</p>
<p>&nbsp;</p>
<p><strong>Update 26.02.2011:</strong><br />
The point of this post is not crazy idea to always rebuild indexes.<br />
Just pay attention when Index (Fast) Full Scan on almost empty table or Index Range Scan without filter predicates return a few rows and does hundreds or thouthands logical reads.<br />
Sometimes myth about degraded indexes is not a myth, and can lead to significant overhead during index scans.</p>
<p>There is data lifecycle pattern which I often meet. There is a table (either heap or IOT) which used for regular inserting and deleting (FIFO/queue like AQ). Normally enqueued/dequeued a little number of rows. And there are periods (or was one period in the past) when a lot amount of data is inserted without dequeuing. For example, when dequeueing is temporary disabled or all enqueuing is perfromed in one transaction. Obviously, after dequeuing will be enabled and rows will be dequeued/deleted index will consists a lot of “empty” leaf blocks.</p>
<p>Below is an example from one production OLTP database. This is the query which executed inside dbms_aq.dequeue:</p>
<p><pre class="brush: plain;">
select tab.row_id,
       tab.msgid,
       tab.corrid,
       tab.priority,
       tab.delay,
       tab.expiration,
       tab.retry_count,
       tab.exception_qschema,
       tab.exception_queue,
       tab.chain_no,
       tab.local_order_no,
       tab.enq_time,
       tab.time_manager_info,
       tab.state,
       tab.enq_tid,
       tab.step_no,
       tab.sender_name,
       tab.sender_address,
       tab.sender_protocol,
       tab.dequeue_msgid,
       tab.user_prop,
       tab.user_data
  from AQ$_SOMEAQTAB_F tab
 where q_name = :1
   and (state = :2)

---------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name                     | Starts | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------
|   1 |  TABLE ACCESS BY INDEX ROWID| SOMEAQTAB                |      1 |      1 |00:00:00.15 |   19959 |
|*  2 |   INDEX RANGE SCAN          | AQ$_SOMEAQTAB_I          |      1 |      1 |00:00:00.15 |   19958 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(&quot;QT&quot;.&quot;Q_NAME&quot;=:1 AND &quot;QT&quot;.&quot;STATE&quot;=:2)
</pre></p>
<p>Notice, 20K logical reads during index range scan which has only access predicates and return 1 row to parent operation.</p>
<p>The index consists thouthands of empty (rrow=0) index leaf blocks<br />
treedump:</p>
<p><pre class="brush: plain;">
----- begin tree dump
branch: 0x405e912 67496210 (0: nrow: 14, level: 3)
   branch: 0x32067894 839284884 (-1: nrow: 37, level: 2)
      branch: 0x6ecbd82f 1858852911 (-1: nrow: 26, level: 1)
         leaf: 0x5363dd92 1399053714 (-1: nrow: 28 rrow: 0)
         leaf: 0x7f472d2a 2135371050 (0: nrow: 20 rrow: 0)
         leaf: 0x5363dd91 1399053713 (1: nrow: 0 rrow: 0)
         leaf: 0x5363dd94 1399053716 (2: nrow: 29 rrow: 0)
         leaf: 0x5363dd93 1399053715 (3: nrow: 30 rrow: 0)
         leaf: 0x5363db51 1399053137 (4: nrow: 10 rrow: 0)
         leaf: 0x5363db52 1399053138 (5: nrow: 0 rrow: 0)
         leaf: 0x5363db54 1399053140 (6: nrow: 0 rrow: 0)
         leaf: 0x5363db53 1399053139 (7: nrow: 30 rrow: 0)
         leaf: 0x5363db57 1399053143 (8: nrow: 31 rrow: 0)
         leaf: 0x5363db58 1399053144 (9: nrow: 27 rrow: 0)
         leaf: 0x5363db71 1399053169 (10: nrow: 6 rrow: 0)
         leaf: 0x5363db7a 1399053178 (11: nrow: 0 rrow: 0)
         leaf: 0x5363db83 1399053187 (12: nrow: 26 rrow: 0)
         leaf: 0x5363db8d 1399053197 (13: nrow: 28 rrow: 0)
         leaf: 0x6ecbd818 1858852888 (14: nrow: 31 rrow: 0)
         leaf: 0x6ecbd81e 1858852894 (15: nrow: 31 rrow: 0)
         leaf: 0x6ecbd825 1858852901 (16: nrow: 31 rrow: 0)
         leaf: 0x6ecbd830 1858852912 (17: nrow: 31 rrow: 0)
         leaf: 0x6ecbd674 1858852468 (18: nrow: 31 rrow: 0)
         leaf: 0x6ecbd67f 1858852479 (19: nrow: 29 rrow: 0)
         leaf: 0x6ecbd688 1858852488 (20: nrow: 29 rrow: 0)
         leaf: 0x6ecbd68f 1858852495 (21: nrow: 30 rrow: 0)
         leaf: 0x5363db1b 1399053083 (22: nrow: 28 rrow: 0)
         leaf: 0x5363db21 1399053089 (23: nrow: 31 rrow: 0)
         leaf: 0x5363db2e 1399053102 (24: nrow: 30 rrow: 0)
      branch: 0x6ecbd3bc 1858851772 (0: nrow: 48, level: 1)
         leaf: 0x5363db49 1399053129 (-1: nrow: 31 rrow: 0)
         leaf: 0x6ecbd618 1858852376 (0: nrow: 0 rrow: 0)
         leaf: 0x6ecbd628 1858852392 (1: nrow: 31 rrow: 0)
         leaf: 0x6ecbd62f 1858852399 (2: nrow: 8 rrow: 0)
         leaf: 0x6ecbd634 1858852404 (3: nrow: 31 rrow: 0)
         ...
         ...
</pre></p>
<p>The query had gotten from an OLTP database where it executed a few times in second!</p>
<p>&nbsp;</p>
<p>p.s.<br />
Useful presentation about index internals from Richard Foote:<br />
<a href="http://richardfoote.files.wordpress.com/2007/12/index-internals-rebuilding-the-truth-ii.pdf">http://richardfoote.files.wordpress.com/2007/12/index-internals-rebuilding-the-truth-ii.pdf</a></p>
<p><strong>Update 03.03.2011</strong><br />
<a href="https://supporthtml.oracle.com/ep/faces/secure/km/DocumentDisplay.jspx?id=271855.1">Note 271855.1</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/alexanderanokhin.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/alexanderanokhin.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/alexanderanokhin.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/alexanderanokhin.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/alexanderanokhin.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/alexanderanokhin.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/alexanderanokhin.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/alexanderanokhin.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/alexanderanokhin.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/alexanderanokhin.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/alexanderanokhin.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/alexanderanokhin.wordpress.com/62/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/alexanderanokhin.wordpress.com/62/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/alexanderanokhin.wordpress.com/62/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=62&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://alexanderanokhin.wordpress.com/2011/02/22/when-indexes-degrade/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/48a7cd21f3eee64ace66d6668a3e1223?s=96&#38;d=http%3A%2F%2Fs0.wp.com%2Fi%2Fmu.gif&#38;r=G" medium="image">
			<media:title type="html">alexanderanokhin</media:title>
		</media:content>
	</item>
		<item>
		<title>Jokes of the CBO with local indexes.</title>
		<link>http://alexanderanokhin.wordpress.com/2011/02/15/jokes-of-the-cbo-with-local-indexes/</link>
		<comments>http://alexanderanokhin.wordpress.com/2011/02/15/jokes-of-the-cbo-with-local-indexes/#comments</comments>
		<pubDate>Tue, 15 Feb 2011 17:30:15 +0000</pubDate>
		<dc:creator>Alexander Anokhin</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[CBO]]></category>

		<guid isPermaLink="false">http://alexanderanokhin.wordpress.com/?p=42</guid>
		<description><![CDATA[It may seem strange, but in some cases estimation of cardinality can depends on size (in blocks) of a segment. 1. Estimation of cardinality can depends on size (in blocks) of a segment. First, I will create table with two partitions with different size. At this point we have a table with two partitions &#8211; [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=42&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>It may seem strange, but in some cases estimation of cardinality can depends on size (in blocks) of a segment.<span id="more-42"></span></p>
<p><strong>1. Estimation of cardinality can depends on size (in blocks) of a segment.</strong><br />
First, I will create table with two partitions with different size.</p>
<p><pre class="brush: plain;">
-- Creation of the table
SQL&gt;
  2  create table test
  3  ( state    varchar2(10)  not null,
  4    val      number(4)     not null
  5  )
  6  partition by list (state)
  7  (
  8    partition p1 values ('Open'),
  9    partition p2 values ('Closed')
 10  );

Table created

-- Populating of the table
SQL&gt;
  2
  3  insert into test
  4      select 'Open',
  5             decode(mod(rownum, 10), 0, 0, 1)
  6        from dual
  7     connect by level &lt;= 10000;   10000 rows inserted SQL&gt;
  2  insert into test
  3      select 'Closed',
  4             100 + mod(rownum, 10)
  5        from dual
  6     connect by level &lt;= 90000;   

90000 rows inserted 

SQL&gt; commit;

Commit complete

SQL&gt;
  2  begin
  3      dbms_stats.gather_table_stats(user,
  4                                    'test',
  5                                    cascade          =&gt; true,
  6                                    granularity      =&gt; 'ALL',
  7                                    estimate_percent =&gt; 100,
  8                                    method_opt       =&gt; 'for all columns size 10'
  9                                    );
 10  end;
 11  /

PL/SQL procedure successfully completed
</pre></p>
<p>At this point we have a table with two partitions &#8211; P1 and P2 with gathered histograms on all coumns. The partition P1 consists of 1000 rows with column VAL = 0 and 9000 rows with VAL = 1.<br />
Let&#8217;s try to see estimation of cardinality</p>
<p><pre class="brush: plain;">
SQL&gt;
  2  explain plan for
  3  SELECT t.*
  4    FROM test t
  5   WHERE t.state = 'Open'
  6     AND t.val = 1;

Explained

SQL&gt; select * from table(dbms_xplan.display);

----------------------------------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |  8999 | 62993 |    23  (48)| 00:00:01 |       |       |
|   1 |  PARTITION LIST SINGLE|      |  8999 | 62993 |    23  (48)| 00:00:01 |   KEY |   KEY |
|*  2 |   TABLE ACCESS FULL   | TEST |  8999 | 62993 |    23  (48)| 00:00:01 |     1 |     1 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(&quot;T&quot;.&quot;VAL&quot;=1)</pre></p>
<p>Oracle correctly estimates cardinality = 8999 (only 1 was lost during rounding).<br />
In this case cardinality of FTS calculated as<br />
selectivity(state = &#8216;Open&#8217;) * selectivity(val = 1) * num_rows (all from P1)<br />
Selectivities are calculated on the basis of histograms.</p>
<p>Let&#8217;s create a local index and look at the plan of the same query.<br />
<pre class="brush: plain;">
SQL&gt; create index testidx on test(val) local;

Index created

SQL&gt;
  2  begin
  3      dbms_stats.gather_index_stats(user,
  4                                    'testidx',
  5                                    granularity      =&gt; 'ALL',
  6                                    estimate_percent =&gt; 100
  7                                    );
  8  end;
  9  /

PL/SQL procedure successfully completed

SQL&gt;
  2  explain plan for
  3  SELECT --+ index(t)
  4         t.*
  5    FROM test t
  6   WHERE t.state = 'Open'
  7     AND t.val = 1;

Explained

SQL&gt; select * from table(dbms_xplan.display);

---------------------------------------------------------------------------------------------------
| Id  | Operation                          | Name    | Rows  | Bytes | Cost (%CPU)| Pstart| Pstop |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |         |  8999 | 62993 |   119  (14)|       |       |
|   1 |  PARTITION LIST SINGLE             |         |  8999 | 62993 |   119  (14)|   KEY |   KEY |
|   2 |   TABLE ACCESS BY LOCAL INDEX ROWID| TEST    |  8999 | 62993 |   119  (14)|     1 |     1 |
|*  3 |    INDEX RANGE SCAN                | TESTIDX |   818 |       |    43  (17)|     1 |     1 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access(&quot;T&quot;.&quot;VAL&quot;=1)
</pre></p>
<p>Cardinality of INDEX RANGE SCAN is 818. Trace 10053 does not contain any info about how this value is calculated.<br />
<pre class="brush: plain; collapse: true; light: false; toolbar: true;">
BASE STATISTICAL INFORMATION
***********************
Table Stats::
  Table: TEST  Alias:  T  Partition [0]
    #Rows: 10000  #Blks:  40  AvgRowLen:  7.00
    #Rows: 10000  #Blks:  40  AvgRowLen:  7.00
Index Stats::
  Index: TESTIDX  Col#: 2  PARTITION [0]
    LVLS: 1  #LB: 40  #DK: 2  LB/K: 20.00  DB/K: 37.00  CLUF: 74.00
    LVLS: 1  #LB: 40  #DK: 2  LB/K: 20.00  DB/K: 37.00  CLUF: 74.00
    User hint to use this index
***************************************
SINGLE TABLE ACCESS PATH
  -----------------------------------------
  BEGIN Single Table Cardinality Estimation
  -----------------------------------------
  Column (#1): STATE(VARCHAR2)  Part#: 0
    AvgLen: 5.00 NDV: 1 Nulls: 0 Density: 5.0000e-05
    Histogram: Freq  #Bkts: 1  UncompBkts: 10000  EndPtVals: 1
  Column (#1): STATE(VARCHAR2)
    AvgLen: 5.00 NDV: 1 Nulls: 0 Density: 5.0000e-05
    Histogram: Freq  #Bkts: 1  UncompBkts: 10000  EndPtVals: 1
  Column (#2): VAL(NUMBER)  Part#: 0
    AvgLen: 3.00 NDV: 2 Nulls: 0 Density: 0.05 Min: 0 Max: 1
    Histogram: Freq  #Bkts: 2  UncompBkts: 10000  EndPtVals: 2
  Column (#2): VAL(NUMBER)
    AvgLen: 3.00 NDV: 2 Nulls: 0 Density: 0.05 Min: 0 Max: 1
    Histogram: Freq  #Bkts: 2  UncompBkts: 10000  EndPtVals: 2
  Table: TEST  Alias: T
    Card: Original: 10000  Rounded: 8999  Computed: 8999.05  Non Adjusted: 8999.05
  -----------------------------------------
  END   Single Table Cardinality Estimation
  -----------------------------------------
  Access Path: index (AllEqRange)
    Index: TESTIDX
    resc_io: 103.00  resc_cpu: 4291511
    ix_sel: 0.89995  ix_sel_with_filters: 0.89995
    Cost: 118.86  Resp: 118.86  Degree: 1
  Best:: AccessPath: IndexRange  Index: TESTIDX
         Cost: 118.86  Degree: 1  Resp: 118.86  Card: 8999.05  Bytes: 0
***************************************
OPTIMIZER STATISTICS AND COMPUTATIONS
***************************************
GENERAL PLANS
***************************************
Considering cardinality-based initial join order.
Permutations for Starting Table :0
***********************
Join order[1]:  TEST[T]#0
***********************
Best so far: Table#: 0  cost: 118.8565  card: 8999.0500  bytes: 62993
(newjo-stop-1) k:0, spcnt:0, perm:1, maxperm:80000
*********************************
Number of join permutations tried: 1
*********************************
Final - All Rows Plan:  Best join order: 1
  Cost: 118.8565  Degree: 1  Card: 8999.0000  Bytes: 62993
  Resc: 118.8565  Resc_io: 103.0000  Resc_cpu: 4291511
  Resp: 118.8565  Resp_io: 103.0000  Resc_cpu: 4291511
kkoipt: Query block SEL$1 (#0)
******* UNPARSED QUERY IS *******
SELECT /*+ INDEX (&quot;T&quot;) */ &quot;T&quot;.&quot;STATE&quot; &quot;STATE&quot;,&quot;T&quot;.&quot;VAL&quot; &quot;VAL&quot; FROM &quot;SA&quot;.&quot;TEST&quot; &quot;T&quot; WHERE &quot;T&quot;.&quot;VAL&quot;=1
kkoqbc-subheap (delete addr=ffffffff7cf189b8, in-use=12592, alloc=16040)
kkoqbc-end
          : call(in-use=20992, alloc=49080), compile(in-use=41208, alloc=44568)
apadrv-end: call(in-use=20992, alloc=49080), compile(in-use=42008, alloc=44568)

sql_id=7vvtk89y1v5zb.
Current SQL statement for this session:
explain plan for
SELECT --+ index(t)
       t.*
  FROM test t
 WHERE t.state = 'Open'
   AND t.val = 1

============
Plan Table
============
------------------------------------------------------+-----------------------+---------------+
| Id  | Operation                           | Name    | Rows  | Bytes | Cost  | Pstart| Pstop |
------------------------------------------------------+-----------------------+---------------+
| 0   | SELECT STATEMENT                    |         |       |       |   119 |       |       |
| 1   |  PARTITION LIST SINGLE              |         |  8999 |   62K |   119 | KEY   | KEY   |
| 2   |   TABLE ACCESS BY LOCAL INDEX ROWID | TEST    |  8999 |   62K |   119 | 1     | 1     |
| 3   |    INDEX RANGE SCAN                 | TESTIDX |   818 |       |    43 | 1     | 1     |
------------------------------------------------------+-----------------------+---------------+
Predicate Information:
----------------------
3 - access(&quot;T&quot;.&quot;VAL&quot;=1)
</pre></p>
<p>In fact Oracle corrects cardinality of INDEX RANGE SCAN by proportion of numblocks(P1) in numblocks(table).</p>
<p>Cardinality in this case calculated as:<br />
Cardinality = estimated cardinality multiplied by numblocks(P1)/numblocks(table)</p>
<p><pre class="brush: plain;">
SQL&gt; select partition_name,
  2         blocks
  3    from dba_tab_statistics s
  4   where table_name = 'TEST'
  5     and (partition_name is null or partition_name = 'P1')
  6  ;

PARTITION_NAME                     BLOCKS
------------------------------ ----------
P1                                     40
                                      440
</pre></p>
<p>Thus, cardinality of IRS = 8999 * 40/440 = 818</p>
<p>It is strange, especially with gathered histograms. In fact figures from histograms gathered inside partitions is enough in this case.<br />
(It is important to note, if column val would be &#8220;nullable&#8221; this effect disappears).<br />
It is the first.</p>
<p>And the second, notice, that step 2 &#8220;TABLE ACCESS BY LOCAL INDEX ROWID&#8221; has right cardinality and cost.<br />
It, as expected, calculated as<br />
blevel + (leaf_blocks + cluf) * selectivity + cpu_cost/(cpuspeed*sreadtim*1000) = 1 + (40 + 74)*0.89995 + cpu_cost/(cpuspeed*sreadtim*1000) = 119<br />
(I&#8217;ve ommitted some minor details)<br />
Looks like that wrong cardinality of IRS is not a big problem, because parent step has right figures.</p>
<p>The script is used in this part:<br />
<pre class="brush: sql; collapse: true; light: false; toolbar: true;">
drop table test;

-- Creation of the table
create table test
( state    varchar2(10)  not null,
  val      number(4)     not null
)
partition by list (state)
(
  partition p1 values ('Open'),
  partition p2 values ('Closed')
);


insert into test
    select 'Open',
           decode(mod(rownum, 10), 0, 0, 1)
      from dual 
   connect by level &lt;= 10000;

insert into test
    select 'Closed',
           100 + mod(rownum, 10)
      from dual 
   connect by level &lt;= 90000;

commit;

begin
    dbms_stats.gather_table_stats(user, 
                                  'test', 
                                  cascade          =&gt; true, 
                                  granularity      =&gt; 'ALL', 
                                  estimate_percent =&gt; 100,
                                  method_opt       =&gt; 'for all columns size 10'
                                  ); 
end;
/

explain plan for
SELECT t.*
  FROM test t
 WHERE t.state = 'Open'
   AND t.val = 1;

select * from table(dbms_xplan.display);

-- Let’s create a local index and look at the plan of the same query.
create index testidx on test(val) local;

begin 
    dbms_stats.gather_index_stats(user, 
                                  'testidx', 
                                  granularity      =&gt; 'ALL', 
                                  estimate_percent =&gt; 100
                                  ); 
end;
/

explain plan for
SELECT --+ index(t)
       t.*
  FROM test t
 WHERE t.state = 'Open'
   AND t.val = 1;

select * from table(dbms_xplan.display);
</pre></p>
<p>Two little additional points:<br />
<strong>2. An oddity with Index Fast Full Scan when table access is not required</strong><br />
What if we change the query to avoid the table lookup and to scan only the index?<br />
as this<br />
<pre class="brush: plain;">
    SELECT count(*)
      FROM test t
     WHERE t.state = 'Open'
       AND t.val = 1;
</pre></p>
<p>Let&#8217;s try to force Oracle to use Index Fast Full Scan:<br />
<pre class="brush: plain;">
SQL&gt; explain plan for
  2      SELECT --+ index_ffs(t testidx)
  3             count(*)
  4        FROM test t
  5       WHERE t.state = 'Open'
  6         AND t.val = 1;
 
Explained

SQL&gt; select * from table(dbms_xplan.display);

----------------------------------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |  8999 | 62993 |    23  (48)| 00:00:01 |       |       |
|   1 |  PARTITION LIST SINGLE|      |  8999 | 62993 |    23  (48)| 00:00:01 |   KEY |   KEY |
|*  2 |   TABLE ACCESS FULL   | TEST |  8999 | 62993 |    23  (48)| 00:00:01 |     1 |     1 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(&quot;T&quot;.&quot;VAL&quot;=1)</pre><br />
Oracle cannot use Index Fast Full Scan here and will be able to do it only if the index will contain column STATE. I suggest that it is because Oracle believes that it needs to visit the table for column STATE. </p>
<p><strong>3. An oddity with the cost of the statement</strong><br />
Fortunately it is enough smart to use Index Range Scan here, for the same query as above, without table access.<br />
From examples above we can expect cost of TABLE ACCESS FULL is 23 and the cost of INDEX RANGE SCAN is 43 for the following query:<br />
<pre class="brush: plain;">
    SELECT count(*)
      FROM test t
     WHERE t.state = 'Open'
       AND t.val = 1;
</pre><br />
TABLE ACCESS FULL is cheaper and it is expected that Oracle will chose full scan.<br />
So, I am going to increase size of the table and do cost of TABLE ACCESS FULL much than INDEX RANGE SCAN.</p>
<p><pre class="brush: plain;">
SQL&gt; declare
  2    i integer;
  3  begin
  4
  5    dbms_stats.set_table_stats(user, 'TEST', 'P1', numblks =&gt; 200);
  6
  7  end;
  8  /

PL/SQL procedure successfully completed
</pre></p>
<p><pre class="brush: plain;">
SQL&gt; explain plan for
  2  SELECT count(*)
  3    FROM test t
  4   WHERE t.state = 'Open'
  5     AND t.val = 1;

Explained

SQL&gt; select * from table(dbms_xplan.display);

------------------------------------------------------------------------------------
| Id  | Operation              | Name | Rows  | Bytes | Cost (%CPU)| Pstart| Pstop |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      |     1 |     7 |     65  (0)|       |       |
|   1 |  SORT AGGREGATE        |      |     1 |     7 |            |       |       |
|   2 |   PARTITION LIST SINGLE|      |  8999 | 62993 |     65  (0)|   KEY |   KEY |
|*  3 |    TABLE ACCESS FULL   | TEST |  8999 | 62993 |     65  (0)|     1 |     1 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter(&quot;T&quot;.&quot;VAL&quot;=1)
</pre><br />
Full scan again. Notice, cost is 65 now. But I expected to see INDEX RANGE SCAN here because it is cheaper, cost of IRS should be = 43.</p>
<p>Let&#8217;s try to force Oracle use index:<br />
<pre class="brush: plain;">
SQL&gt; explain plan for
  2  SELECT --+ index(t)
  3         count(*)
  4    FROM test t
  5   WHERE t.state = 'Open'
  6     AND t.val = 1;

Explained

SQL&gt; select * from table(dbms_xplan.display);

---------------------------------------------------------------------------------------
| Id  | Operation              | Name    | Rows  | Bytes | Cost (%CPU)| Pstart| Pstop |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |         |     1 |     7 |   119   (1)|       |       |
|   1 |  SORT AGGREGATE        |         |     1 |     7 |            |       |       |
|   2 |   PARTITION LIST SINGLE|         |  8999 | 62993 |    43   (0)|   KEY |   KEY |
|*  3 |    INDEX RANGE SCAN    | TESTIDX |  8999 | 62993 |    43   (0)|     1 |     1 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access(&quot;T&quot;.&quot;VAL&quot;=1)
</pre><br />
Pay attention to the costs of the step 3 (=43) and especially step 1.<br />
Cost of the step 1 = 119 it is the cost of TABLE ACCESS BY INDEX ROWID in the plan above. Oracle continues to take into account cost of table lookup (clustering factor) when such step does not exist in the plan!</p>
<p>Personally I consider this as a bug. Oracle uses CLUF in the estimations for a plan without TABLE ACCESS BY INDEX ROWID, it is absolutely wrong.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/alexanderanokhin.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/alexanderanokhin.wordpress.com/42/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/alexanderanokhin.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/alexanderanokhin.wordpress.com/42/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/alexanderanokhin.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/alexanderanokhin.wordpress.com/42/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/alexanderanokhin.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/alexanderanokhin.wordpress.com/42/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/alexanderanokhin.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/alexanderanokhin.wordpress.com/42/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/alexanderanokhin.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/alexanderanokhin.wordpress.com/42/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/alexanderanokhin.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/alexanderanokhin.wordpress.com/42/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=42&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://alexanderanokhin.wordpress.com/2011/02/15/jokes-of-the-cbo-with-local-indexes/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/48a7cd21f3eee64ace66d6668a3e1223?s=96&#38;d=http%3A%2F%2Fs0.wp.com%2Fi%2Fmu.gif&#38;r=G" medium="image">
			<media:title type="html">alexanderanokhin</media:title>
		</media:content>
	</item>
		<item>
		<title>CBO defaults</title>
		<link>http://alexanderanokhin.wordpress.com/2011/02/08/cbo-defaults/</link>
		<comments>http://alexanderanokhin.wordpress.com/2011/02/08/cbo-defaults/#comments</comments>
		<pubDate>Tue, 08 Feb 2011 16:06:58 +0000</pubDate>
		<dc:creator>Alexander Anokhin</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[CBO]]></category>

		<guid isPermaLink="false">http://alexanderanokhin.wordpress.com/?p=17</guid>
		<description><![CDATA[Somtime ago I was need to determine how selectivity is calculated in a simple query: There are two questions: 1. Cardinality of INDEX RANGE SCAN less(!) than TABLE ACCESS BY INDEX ROWID 2. How cardinality of INDEX RANGE SCAN is calculated The answer and other usefull CBO defaults were found here: http://www.mail-archive.com/oracle-l@fatcity.com/msg24731.html Oracle uses different [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=17&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Somtime ago I was need to determine how selectivity is calculated in a simple query:<br />
<pre class="brush: plain;">
SQL&gt; create table all_obj as
  2    select rownum id,
  3           rownum n
  4      from all_objects
  5     where rownum &lt;= 10000;
 
Table created

SQL&gt; create index idx on all_obj(n);
 
Index created

SQL&gt; explain plan for
  2    select *
  3      from all_obj
  4     where n = :1;
 
Explained

SQL&gt; select * from table(dbms_xplan.display);
 
PLAN_TABLE_OUTPUT
-------------------------------------------------------
| Id  | Operation                   | Name    | Rows  |
-------------------------------------------------------
|   0 | SELECT STATEMENT            |         |   100 |
|   1 |  TABLE ACCESS BY INDEX ROWID| ALL_OBJ |   100 |
|*  2 |   INDEX RANGE SCAN          | IDX     |    40 |
-------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(&quot;N&quot;=TO_NUMBER(:1))
Note
-----
   - dynamic sampling used for this statement
 
</pre></p>
<p>There are two questions:<br />
1. Cardinality of INDEX RANGE SCAN less(!) than TABLE ACCESS BY INDEX ROWID<br />
2. How cardinality of INDEX RANGE SCAN is calculated</p>
<p>The answer and other usefull CBO defaults were found here:<br />
<a href="http://www.mail-archive.com/oracle-l@fatcity.com/msg24731.html">http://www.mail-archive.com/oracle-l@fatcity.com/msg24731.html</a><br />
<pre class="brush: plain;">
=========================================================
/* defaults */

/* 
Default selectivities are set low to
1. keep cost values low for future resource limiter use
2. keep cost values low for permutation cutoff in kko
Defaults are used for bind variables, general expressions and
unanalyzed tables, except for equality where defaults are not
needed for bind variables.
*/

#define KKEDSREL 0.05 /* default selectivity for &lt; &lt;= &gt; &gt;= */
#define KKEDSEQ 0.01 /* default selectivity for = */
#define KKEDSNE 0.05 /* default selectivity for != */
#define KKEDSDF 0.05 /* default selectivity for all other ops */
#define KKEDSIRL 0.009 /* default selectivity for relation on indexed col */
#define KKEDSBRL 0.009 /* def sel for relation with bind var on index col*/
#define KKEDSIEQ 0.004 /* default selectivity for = on indexed col */
#define KKEDMBR 8 /* default multiblock read factor */
#define KKEDMBW 8 /* default multiblock write factor */
#define KKEDFNR 100.0 /* default - fixed table cardinality */
#define KKEDFRL 20 /* default - fixed table row length */
#define KKEDDNR 2000.0 /* default - remote table cardinality */
#define KKEDDRL 100 /* default - remote table avg row length */
#define KKEDDNB 100 /* default - default # of blocks */
#define KKEDDSC 13.0 /* default - default scan cost */
#define KKEDILV 1 /* default - default index levels */
#define KKEDILB 25 /* default - number of index leaf blocks */
#define KKEDLBK 1 /* default - number leaf blocks/key */
#define KKEDDBK 1 /* default - number of data blocks/key */
#define KKEDKEY 100 /* default - number of distinct keys */
#define KKEDCLF (KKEDDNB*8) /* default - clustering factor */
#define KKECRI 1.5 /* remote table access cost increase factor */
#define KKECFSC 1.0 /* fixed table scan cost */
#define KKECFNB 0 /* fixed table number of blocks */
#define KKECMXB 15 /* maximum byte length for normalization */
#define KKECBBS 256.0 /* base for byte sequence normalization */
#define KKECSPC ' ' /* space byte value */
#define KKECSPD 86400.0 /* seconds per day */
#define KKESROH 10.0 /* sort per row overhead in bytes */
#define KKESAUT 0.75 /* sort area utilization */
#define KKESROP 0.10 /* sort row overhead percent */
#define KKESRML 2.0 /* sort run multiple */
#define KKESTP 0x01 /* single table predicate */
#define KKETEQ 0x02 /* equi join */
#define KKETBCPJ 0x04 /* Cartesian product join */
#define KKESOK 0x08 /* input swap ok */
#define KKESWP 0x10 /* inputs swapped */
#define KKEEQP 0x20 /* equipartitioned */
#define KKELKNWC 0x01 /* LIKE no wild card */
#define KKELKTWC 0x02 /* LIKE trailing wild card */
#define KKELKEWC 0x04 /* LIKE embedded wild card */
#define KKELKLWC 0x08 /* LIKE leading wild card */
#define KKELKOWC 0x10 /* LIKE only wild card */
==================================================================
</pre></p>
<p>Oracle uses different constants for estimation of default selectivity of a table and an index range scan:<br />
<pre class="brush: plain;">
#define KKEDSEQ 0.01 /* default selectivity for = */
#define KKEDSIEQ 0.004 /* default selectivity for = on indexed col */
</pre><br />
So, for the execution plan above<br />
<pre class="brush: plain;">
-------------------------------------------------------
| Id  | Operation                   | Name    | Rows  |
-------------------------------------------------------
|   0 | SELECT STATEMENT            |         |   100 |
|   1 |  TABLE ACCESS BY INDEX ROWID| ALL_OBJ |   100 |
|*  2 |   INDEX RANGE SCAN          | IDX     |    40 |
-------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(&quot;N&quot;=TO_NUMBER(:1))
</pre><br />
cardinality of TABLE ACCESS BY INDEX ROWID = 10000 * 0.01 = 100<br />
cardinality of INDEX RANGE SCAN = 10000 * 0.004 = 40</p>
<p>10000 is number of rows in the table getting during dynamic sampling.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/alexanderanokhin.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/alexanderanokhin.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/alexanderanokhin.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/alexanderanokhin.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/alexanderanokhin.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/alexanderanokhin.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/alexanderanokhin.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/alexanderanokhin.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/alexanderanokhin.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/alexanderanokhin.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/alexanderanokhin.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/alexanderanokhin.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/alexanderanokhin.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/alexanderanokhin.wordpress.com/17/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=17&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://alexanderanokhin.wordpress.com/2011/02/08/cbo-defaults/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/48a7cd21f3eee64ace66d6668a3e1223?s=96&#38;d=http%3A%2F%2Fs0.wp.com%2Fi%2Fmu.gif&#38;r=G" medium="image">
			<media:title type="html">alexanderanokhin</media:title>
		</media:content>
	</item>
		<item>
		<title>,,,=^..^=,,,</title>
		<link>http://alexanderanokhin.wordpress.com/2010/11/17/3/</link>
		<comments>http://alexanderanokhin.wordpress.com/2010/11/17/3/#comments</comments>
		<pubDate>Wed, 17 Nov 2010 07:55:08 +0000</pubDate>
		<dc:creator>Alexander Anokhin</dc:creator>
				<category><![CDATA[Oracle]]></category>

		<guid isPermaLink="false">http://alexanderanokhin.wordpress.com/?p=3</guid>
		<description><![CDATA[Hello World! Here is my blog.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=3&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Hello World!<br />
Here is my blog.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/alexanderanokhin.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/alexanderanokhin.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/alexanderanokhin.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/alexanderanokhin.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/alexanderanokhin.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/alexanderanokhin.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/alexanderanokhin.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/alexanderanokhin.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/alexanderanokhin.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/alexanderanokhin.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/alexanderanokhin.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/alexanderanokhin.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/alexanderanokhin.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/alexanderanokhin.wordpress.com/3/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=alexanderanokhin.wordpress.com&amp;blog=17752444&amp;post=3&amp;subd=alexanderanokhin&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://alexanderanokhin.wordpress.com/2010/11/17/3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/48a7cd21f3eee64ace66d6668a3e1223?s=96&#38;d=http%3A%2F%2Fs0.wp.com%2Fi%2Fmu.gif&#38;r=G" medium="image">
			<media:title type="html">alexanderanokhin</media:title>
		</media:content>
	</item>
	</channel>
</rss>
