<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;CUABQno_eCp7ImA9WhRaE0U.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730</id><updated>2012-02-16T14:45:53.440+05:30</updated><title>Oracle Experiences and information</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://sandeepredkar.blogspot.com/" /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>12</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/SandeepRedkar" /><feedburner:info uri="sandeepredkar" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>SandeepRedkar</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><entry gd:etag="W/&quot;A0QNQHg9cSp7ImA9WxBSE00.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-3587524277248391266</id><published>2009-12-19T14:45:00.003+05:30</published><updated>2009-12-20T17:13:11.669+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-20T17:13:11.669+05:30</app:edited><title>Index column ordering and performance</title><content type="html">When we are talking about why oracle is not picking up index, one of the well known reason is cardinality. When data is uniform, an index is selective only when cardinality is not very low. When data is not uniform, we can have histograms but here we will keep that feature apart. &lt;br /&gt;&lt;br /&gt;So lets assume we want to create a composite index on two columns which both are part of the filter condition. One column (ID) is having high cardinality and another (Status) is having very low cardinality. Then what should be the column sequence for that index? The obivious answer is, for better performance first column should be one which is having high cardinality. &lt;br /&gt;&lt;br /&gt;But in reality, any column sequence will give the same performance. To demonstrate lets create following testcase. We will create a table and will choose two columns object_id and status. The column object_id is distinct and update the status column such that it will only two values with same number of rows. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create table t as select * from dba_objects;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; update t set status=decode(mod(rownum,2),0,'VALID','INVALID');&lt;br /&gt;&lt;br /&gt;50623 rows updated.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select status, count(1) from t group by status;&lt;br /&gt;&lt;br /&gt;STATUS    COUNT(1)&lt;br /&gt;------- ----------&lt;br /&gt;INVALID      25312&lt;br /&gt;VALID        25311&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create index ind_t on t(object_id, status);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats(user,'T',cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select * from t where object_id=100 and status='INVALID';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 4013845416&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |       |     1 |    94 |     2   (0)| 00:00:01 |&lt;br /&gt;|   1 |  TABLE ACCESS BY INDEX ROWID| T     |     1 |    94 |     2   (0)| 00:00:01 |&lt;br /&gt;|*  2 |   INDEX RANGE SCAN          | IND_T |     1 |       |     1   (0)| 00:00:01 |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - access("OBJECT_ID"=100 AND "STATUS"='INVALID')&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          1  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;          4  consistent gets&lt;br /&gt;          0  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;       1207  bytes sent via SQL*Net to client&lt;br /&gt;        396  bytes received via SQL*Net from client&lt;br /&gt;          2  SQL*Net roundtrips to/from client&lt;br /&gt;          0  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;          1  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The query executed with 4 consistent gets. Now lets create the index with reverse order and check the performance.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; drop index ind_t;&lt;br /&gt;&lt;br /&gt;Index dropped.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create index ind_t on t(status, object_id);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats(user,'T',cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select * from t where object_id=100 and status='INVALID';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 4013845416&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |       |     1 |    94 |     2   (0)| 00:00:01 |&lt;br /&gt;|   1 |  TABLE ACCESS BY INDEX ROWID| T     |     1 |    94 |     2   (0)| 00:00:01 |&lt;br /&gt;|*  2 |   INDEX RANGE SCAN          | IND_T |     1 |       |     1   (0)| 00:00:01 |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - access("STATUS"='INVALID' AND "OBJECT_ID"=100)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          1  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;          4  consistent gets&lt;br /&gt;          0  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;       1207  bytes sent via SQL*Net to client&lt;br /&gt;        396  bytes received via SQL*Net from client&lt;br /&gt;          2  SQL*Net roundtrips to/from client&lt;br /&gt;          0  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;          1  rows processed&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Even after changing the column order, the query got executed with 4 consistent gets. So no performance degradation. &lt;br /&gt;&lt;br /&gt;So now we have prooved that column order is having no performance on the query. &lt;br /&gt;&lt;br /&gt;But this column order may cause performance degradation. Lets assume, In the application, we have some query only with object_id as a filter. If there is no individual index on object_id column, then oracle will use this index and here column ordering will impact. &lt;br /&gt;&lt;br /&gt;We will execute the following query with index having order (object_id, status) and lets check the execution plan. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select * from t where object_id=100;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 4013845416&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |       |     1 |    94 |     3   (0)| 00:00:01 |&lt;br /&gt;|   1 |  TABLE ACCESS BY INDEX ROWID| T     |     1 |    94 |     3   (0)| 00:00:01 |&lt;br /&gt;|*  2 |   INDEX RANGE SCAN          | IND_T |     1 |       |     2   (0)| 00:00:01 |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - access("OBJECT_ID"=100)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          1  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;          4  consistent gets&lt;br /&gt;          0  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;       1207  bytes sent via SQL*Net to client&lt;br /&gt;        396  bytes received via SQL*Net from client&lt;br /&gt;          2  SQL*Net roundtrips to/from client&lt;br /&gt;          0  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;          1  rows processed&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It is doing Index range scan with 4 consistent gets. Lets reverse the column order (status, object_id) and check the execution plan.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select * from t where object_id=100;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 2246305531&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |       |     1 |    94 |     4   (0)| 00:00:01 |&lt;br /&gt;|   1 |  TABLE ACCESS BY INDEX ROWID| T     |     1 |    94 |     4   (0)| 00:00:01 |&lt;br /&gt;|*  2 |   INDEX SKIP SCAN           | IND_T |     1 |       |     3   (0)| 00:00:01 |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - access("OBJECT_ID"=100)&lt;br /&gt;       filter("OBJECT_ID"=100)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          1  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;          8  consistent gets&lt;br /&gt;          0  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;       1207  bytes sent via SQL*Net to client&lt;br /&gt;        396  bytes received via SQL*Net from client&lt;br /&gt;          2  SQL*Net roundtrips to/from client&lt;br /&gt;          0  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;          1  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Oracle has opted for index skip scan and consistent gets are also double against our previous execution plan.&lt;br /&gt;&lt;br /&gt;In the above example, other column is having only 4 distinct values, but if the distinct values are higher then oracle may ignore this index and will opt for Full table scan.&lt;br /&gt;&lt;br /&gt;To sum up, when query is having multiple filter conditions, column order of a composite index on those columns does not matter. But it may have impact on other queries having not all filter conditions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-3587524277248391266?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/yRFkOo8i1BQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/3587524277248391266/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=3587524277248391266&amp;isPopup=true" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/3587524277248391266?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/3587524277248391266?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/yRFkOo8i1BQ/index-column-ordering-and-performance.html" title="Index column ordering and performance" /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2009/12/index-column-ordering-and-performance.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUUCQn0zfip7ImA9WxBSEUk.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-7061774097347892196</id><published>2009-12-18T19:01:00.002+05:30</published><updated>2009-12-18T19:04:23.386+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-18T19:04:23.386+05:30</app:edited><title>Query and Transitive Closure</title><content type="html">I have got some of the comments on my last blog - "Query Performance &amp; Join Conditions" which were asking about the transitive closure. The example that I have given was not for transitive closure. Oracle has introduced transitive &lt;br /&gt;&lt;br /&gt;closure when the literal values are present for joining conditions. For example when I have join condition on two tables and same column of one of the table is having filter condition then other table will use the same filter condition. &lt;br /&gt;&lt;br /&gt;To demonstrate, we will see the following test case.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create table t1 as select rownum "N", rownum*30 "M" from dual connect by level &lt;=10000;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create table t2 as select rownum+20 "R", rownum*50 "T" from dual connect by level &lt;=10000;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create index ind_t1 on t1(n);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create index ind_t2 on t2(r);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats(user,'T1',cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats(user,'T2',cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; set autot trace exp stat&lt;br /&gt;SQL&gt; select n, m, t&lt;br /&gt;  2  from       t1, t2&lt;br /&gt;  3  where      t1.n = t2.r&lt;br /&gt;  4  and        t1.n=30;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 863060763&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                     | Name   | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;----------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT              |        |     1 |    16 |     4   (0)| 00:00:01 |&lt;br /&gt;|   1 |  MERGE JOIN CARTESIAN         |        |     1 |    16 |     4   (0)| 00:00:01 |&lt;br /&gt;|   2 |   TABLE ACCESS BY INDEX ROWID | T1     |     1 |     8 |     2   (0)| 00:00:01 |&lt;br /&gt;|*  3 |    INDEX RANGE SCAN           | IND_T1 |     1 |       |     1   (0)| 00:00:01 |&lt;br /&gt;|   4 |   BUFFER SORT                 |        |     1 |     8 |     2   (0)| 00:00:01 |&lt;br /&gt;|   5 |    TABLE ACCESS BY INDEX ROWID| T2     |     1 |     8 |     2   (0)| 00:00:01 |&lt;br /&gt;|*  6 |     INDEX RANGE SCAN          | IND_T2 |     1 |       |     1   (0)| 00:00:01 |&lt;br /&gt;----------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   3 - access("T1"."N"=30)&lt;br /&gt;   6 - access("T2"."R"=30)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          0  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;          7  consistent gets&lt;br /&gt;          0  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;        512  bytes sent via SQL*Net to client&lt;br /&gt;        396  bytes received via SQL*Net from client&lt;br /&gt;          2  SQL*Net roundtrips to/from client&lt;br /&gt;          1  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;          1  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Check for "Predicate Information" in above-mentioned execution plan. When I have assigned n=30, it has also picked up r=30. Now one might have question, what is the use of this and will it impact performance? &lt;br /&gt;&lt;br /&gt;Based on the input filter condition, oracle may opt for better execution plan.&lt;br /&gt;To demonstrate, lets create t2 table with R column as 4 distinct values and execute the above query again.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create table t1 as select rownum "N", rownum*30 "M" from dual connect by level &lt;=10000;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create table t2 as select mod(rownum,4) "R", rownum*50 "T" from dual connect by level &lt;=10000;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create index ind_t1 on t1(n);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create index ind_t2 on t2(r);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats(user,'T1',cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats(user,'T2',cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; set autot trace exp stat&lt;br /&gt;SQL&gt; select n, m, t&lt;br /&gt;  2  from       t1, t2&lt;br /&gt;  3  where      t1.n = t2.r&lt;br /&gt;  4  and        t2.r=3;&lt;br /&gt;&lt;br /&gt;2500 rows selected.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 2470703826&lt;br /&gt;&lt;br /&gt;---------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                    | Name   | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;---------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT             |        |  2500 | 37500 |     9  (12)| 00:00:01 |&lt;br /&gt;|*  1 |  HASH JOIN                   |        |  2500 | 37500 |     9  (12)| 00:00:01 |&lt;br /&gt;|   2 |   TABLE ACCESS BY INDEX ROWID| T1     |     1 |     8 |     2   (0)| 00:00:01 |&lt;br /&gt;|*  3 |    INDEX RANGE SCAN          | IND_T1 |     1 |       |     1   (0)| 00:00:01 |&lt;br /&gt;|*  4 |   TABLE ACCESS FULL          | T2     |  2500 | 17500 |     6   (0)| 00:00:01 |&lt;br /&gt;---------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - access("T1"."N"="T2"."R")&lt;br /&gt;   3 - access("T1"."N"=3)&lt;br /&gt;   4 - filter("T2"."R"=3)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As per the above plan, based on filter condition(N=3), oracle opted for index scan. This is because of transitive closure. Now what would have happen if transitive closure was not taken place.&lt;br /&gt;For this, lets modify query as follows -&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select n, m, t&lt;br /&gt;  2  from       t1, (select /*+ no_merge */ * from t2 where r=3) t2&lt;br /&gt;  3  where      t1.n = t2.r;&lt;br /&gt;&lt;br /&gt;2500 rows selected.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 157564253&lt;br /&gt;&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT    |      |  2500 | 90000 |    14   (8)| 00:00:01 |&lt;br /&gt;|*  1 |  HASH JOIN          |      |  2500 | 90000 |    14   (8)| 00:00:01 |&lt;br /&gt;|   2 |   VIEW              |      |  2500 | 70000 |     6   (0)| 00:00:01 |&lt;br /&gt;|*  3 |    TABLE ACCESS FULL| T2   |  2500 | 17500 |     6   (0)| 00:00:01 |&lt;br /&gt;|   4 |   TABLE ACCESS FULL | T1   | 10000 | 80000 |     7   (0)| 00:00:01 |&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - access("T1"."N"="T2"."R")&lt;br /&gt;   3 - filter("R"=3)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If we execute same query, oracle would have opted for nested loops.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Conclusion : &lt;/strong&gt;Transitive closure is the feature which works when we are using filter predicate in query with join condition. Oracle will consider transitive closure with all equility, non-equility predicates.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-7061774097347892196?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/imr1qkVZZwE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/7061774097347892196/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=7061774097347892196&amp;isPopup=true" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/7061774097347892196?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/7061774097347892196?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/imr1qkVZZwE/query-and-transitive-closure.html" title="Query and Transitive Closure" /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2009/12/query-and-transitive-closure.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkEAR3k5cCp7ImA9WxNQFkw.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-9168756502559027416</id><published>2009-09-22T15:43:00.003+05:30</published><updated>2009-09-22T15:47:26.728+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-22T15:47:26.728+05:30</app:edited><title>Query Performance &amp; Join Conditions</title><content type="html">Oracle database performance is measured by application queries those are having impact on host resources. Oracle generates plan as per the input values and that decides the resource usage effeciency.&lt;br /&gt;&lt;br /&gt;While working on performance issue, one of the problem reported by the user that one of the module is not functioning and having some issue with query. When we traced the session found one of the query doing FTS and retriving few records only. This was one of the top resource consumer query in the trace file. &lt;br /&gt;&lt;br /&gt;The query was looking very normal and checked for the indexes, statistics of the tables etc. Though the statistics were collected recently, it was not the problem with that. &lt;br /&gt;&lt;br /&gt;After investigation, it was found that one of the condition was not correctly defined and the same has been confirmed by developer. When you have multi table join in the query, we have to be very careful about equility predicates.&lt;br /&gt;&lt;br /&gt;Here I want to remind one of the fact in oracle -&lt;br /&gt;As per mathematics rules, when we say a=b and b=c, we can conclude a=c. But in oracle this is not the case. Oracle never concludes a=c.&lt;br /&gt;&lt;br /&gt;To illustrate this, let us create a test case -&lt;br /&gt;&lt;br /&gt;Create two tables and insert some random records in it. Also create required indexes. As the query provided was from the production, here I am not using the same table names. Also I have picked up only the problematic part of the query.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create table cu_all (custid number, addr varchar2(200), ph number, cano number, acctype varchar2(10));&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create table ca_receipt (custid number, caamt number, cadt date, totbal number);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; insert into cu_all&lt;br /&gt;  2  select     lvl,&lt;br /&gt;  3          dbms_random.string('A',30),&lt;br /&gt;  4          round(dbms_random.value(1,100000)),&lt;br /&gt;  5          round(dbms_random.value(1,10000)),&lt;br /&gt;  6          dbms_random.string('A',10)&lt;br /&gt;  7  from       (select level "LVL" from dual connect by level &lt;=200000);&lt;br /&gt;&lt;br /&gt;200000 rows created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into ca_receipt&lt;br /&gt;  2  select     round(dbms_random.value(1,10000)),&lt;br /&gt;  3          round(dbms_random.value(1,100000)),&lt;br /&gt;  4          sysdate - round(dbms_random.value(1,100000)),&lt;br /&gt;  5          round(dbms_random.value(1,100000))&lt;br /&gt;  6  from       (select level "LVL" from dual connect by level &lt;=500000);&lt;br /&gt;&lt;br /&gt;500000 rows created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create unique index pk_cu_all_ind on cu_all(custid);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create index ind2_cu_all on cu_all(CANO);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create index ind_ca_receipt_custid on ca_receipt(custid);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats(user,'CU_ALL', cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats(user,'CA_RECEIPT', cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now let us execute the query with trace on. This is the similar query which was provided to me.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; set autot trace&lt;br /&gt;SQL&gt; SELECT     ca.*, cu.*&lt;br /&gt;  2  FROM ca_receipt CA,&lt;br /&gt;  3       cu_all CU&lt;br /&gt;  4  WHERE       CA.CUSTID = CU.CUSTID&lt;br /&gt;  5  AND         CA.CUSTID IN (SELECT CUSTID FROM cu_all START WITH custid = 2353&lt;br /&gt;  6                CONNECT BY PRIOR CUSTID = CANO)&lt;br /&gt;  7  ORDER BY ACCTYPE DESC;&lt;br /&gt;&lt;br /&gt;289 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;Pre&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 3186098611&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                           | Name                  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT                    |                       |  1000 | 81000 |   504   (2)| 00:00:07 |&lt;br /&gt;|   1 |  SORT ORDER BY                      |                       |  1000 | 81000 |   504   (2)| 00:00:07 |&lt;br /&gt;|*  2 |   HASH JOIN                         |                       |  1000 | 81000 |   503   (2)| 00:00:07 |&lt;br /&gt;|   3 |    NESTED LOOPS                     |                       |       |       |            |          |&lt;br /&gt;|   4 |     NESTED LOOPS                    |                       |  1000 | 26000 |   112   (1)| 00:00:02 |&lt;br /&gt;|   5 |      VIEW                           | VW_NSO_1              |    20 |   100 |    21   (0)| 00:00:01 |&lt;br /&gt;|   6 |       HASH UNIQUE                   |                       |    20 |   180 |            |          |&lt;br /&gt;|*  7 |        CONNECT BY WITH FILTERING    |                       |       |       |            |          |&lt;br /&gt;|   8 |         TABLE ACCESS BY INDEX ROWID | CU_ALL                |     1 |     9 |     2   (0)| 00:00:01 |&lt;br /&gt;|*  9 |          INDEX UNIQUE SCAN          | PK_CU_ALL_IND         |     1 |       |     1   (0)| 00:00:01 |&lt;br /&gt;|  10 |         NESTED LOOPS                |                       |       |       |            |          |&lt;br /&gt;|  11 |          CONNECT BY PUMP            |                       |       |       |            |          |&lt;br /&gt;|  12 |          TABLE ACCESS BY INDEX ROWID| CU_ALL                |    20 |   180 |    21   (0)| 00:00:01 |&lt;br /&gt;|* 13 |           INDEX RANGE SCAN          | IND2_CU_ALL           |    20 |       |     1   (0)| 00:00:01 |&lt;br /&gt;|* 14 |      INDEX RANGE SCAN               | IND_CA_RECEIPT_CUSTID |    50 |       |     2   (0)| 00:00:01 |&lt;br /&gt;|  15 |     TABLE ACCESS BY INDEX ROWID     | CA_RECEIPT            |    50 |  1050 |    52   (0)| 00:00:01 |&lt;br /&gt;|  16 |    TABLE ACCESS FULL                | CU_ALL                |   200K|    10M|   389   (1)| 00:00:05 |&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   2 - access("CA"."CUSTID"="CU"."CUSTID")&lt;br /&gt;   7 - access("CANO"=PRIOR "CUSTID")&lt;br /&gt;   9 - access("CUSTID"=2353)&lt;br /&gt;  13 - access("CANO"=PRIOR "CUSTID")&lt;br /&gt;  14 - access("CA"."CUSTID"="CUSTID")&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          1  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;       2249  consistent gets&lt;br /&gt;         25  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;      11748  bytes sent via SQL*Net to client&lt;br /&gt;        729  bytes received via SQL*Net from client&lt;br /&gt;         21  SQL*Net roundtrips to/from client&lt;br /&gt;          7  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;        289  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you look at the query, it seems to be normal one. &lt;br /&gt;&lt;br /&gt;But the problem is here- &lt;br /&gt;&lt;br /&gt;Query is having two tables CA and CU. From the inner CU table query, it fetches records and joins with CA table an CA table Joins with CU table using the same column.&lt;br /&gt;Here the inner query joins with CA table and cardinality of the query gets changed. So it is opting FTS when joining to CU table again.&lt;br /&gt;This is causing the performance bottleneck. So to resolve the issue, I have change the joining condition. &lt;br /&gt;&lt;br /&gt;Now if we check, following is the proper execution plan. Also the consistents gets have been reduced to 797 against 2249 in original query.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; SELECT     ca.*, cu.*&lt;br /&gt;  2  FROM ca_receipt CA,&lt;br /&gt;  3       cu_all CU&lt;br /&gt;  4  WHERE       CA.CUSTID = CU.CUSTID&lt;br /&gt;  5  AND         CU.CUSTID IN (SELECT CUSTID FROM cu_all START WITH custid = 2353&lt;br /&gt;  6                CONNECT BY PRIOR CUSTID = CANO)&lt;br /&gt;  7  ORDER BY ACCTYPE DESC;&lt;br /&gt;&lt;br /&gt;289 rows selected.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 3713271440&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                           | Name                  | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT                    |                       |  1000 | 81000 |   133   (2)| 00:00:02 |&lt;br /&gt;|   1 |  SORT ORDER BY                      |                       |  1000 | 81000 |   133   (2)| 00:00:02 |&lt;br /&gt;|   2 |   NESTED LOOPS                      |                       |       |       |            |          |&lt;br /&gt;|   3 |    NESTED LOOPS                     |                       |  1000 | 81000 |   132   (1)| 00:00:02 |&lt;br /&gt;|   4 |     NESTED LOOPS                    |                       |    20 |  1200 |    42   (3)| 00:00:01 |&lt;br /&gt;|   5 |      VIEW                           | VW_NSO_1              |    20 |   100 |    21   (0)| 00:00:01 |&lt;br /&gt;|   6 |       HASH UNIQUE                   |                       |    20 |   180 |            |          |&lt;br /&gt;|*  7 |        CONNECT BY WITH FILTERING    |                       |       |       |            |          |&lt;br /&gt;|   8 |         TABLE ACCESS BY INDEX ROWID | CU_ALL                |     1 |     9 |     2   (0)| 00:00:01 |&lt;br /&gt;|*  9 |          INDEX UNIQUE SCAN          | PK_CU_ALL_IND         |     1 |       |     1   (0)| 00:00:01 |&lt;br /&gt;|  10 |         NESTED LOOPS                |                       |       |       |            |          |&lt;br /&gt;|  11 |          CONNECT BY PUMP            |                       |       |       |            |          |&lt;br /&gt;|  12 |          TABLE ACCESS BY INDEX ROWID| CU_ALL                |    20 |   180 |    21   (0)| 00:00:01 |&lt;br /&gt;|* 13 |           INDEX RANGE SCAN          | IND2_CU_ALL           |    20 |       |     1   (0)| 00:00:01 |&lt;br /&gt;|  14 |      TABLE ACCESS BY INDEX ROWID    | CU_ALL                |     1 |    55 |     1   (0)| 00:00:01 |&lt;br /&gt;|* 15 |       INDEX UNIQUE SCAN             | PK_CU_ALL_IND         |     1 |       |     0   (0)| 00:00:01 |&lt;br /&gt;|* 16 |     INDEX RANGE SCAN                | IND_CA_RECEIPT_CUSTID |    50 |       |     2   (0)| 00:00:01 |&lt;br /&gt;|  17 |    TABLE ACCESS BY INDEX ROWID      | CA_RECEIPT            |    50 |  1050 |    52   (0)| 00:00:01 |&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   7 - access("CANO"=PRIOR "CUSTID")&lt;br /&gt;   9 - access("CUSTID"=2353)&lt;br /&gt;  13 - access("CANO"=PRIOR "CUSTID")&lt;br /&gt;  15 - access("CU"."CUSTID"="CUSTID")&lt;br /&gt;  16 - access("CA"."CUSTID"="CU"."CUSTID")&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          1  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;        797  consistent gets&lt;br /&gt;          1  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;      11748  bytes sent via SQL*Net to client&lt;br /&gt;        729  bytes received via SQL*Net from client&lt;br /&gt;         21  SQL*Net roundtrips to/from client&lt;br /&gt;          7  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;        289  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Hence to sum up, oracle gives us the output based on our input values/conditions. Any query must have proper joining condition when multiple tables are involved.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-9168756502559027416?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/d2ory5KqIHI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/9168756502559027416/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=9168756502559027416&amp;isPopup=true" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/9168756502559027416?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/9168756502559027416?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/d2ory5KqIHI/query-performance-join-conditions.html" title="Query Performance &amp; Join Conditions" /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2009/09/query-performance-join-conditions.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkcGRnozeCp7ImA9WxJWFUo.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-6373441107357559495</id><published>2009-06-21T13:01:00.006+05:30</published><updated>2009-06-21T13:10:27.480+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-21T13:10:27.480+05:30</app:edited><title>ORA-600 [ktadrprc-1]</title><content type="html">During my visit to the one of the customer, I found following error while dropping old partition from partitioned table.&lt;br /&gt;&lt;br /&gt;ORA-00600: internal error code, arguments: [ktadrprc-1], [], [], [], [], [], [], []&lt;br /&gt;&lt;br /&gt;When we search metalink, it was found that this error can occur when there is mismatch between seg$ and ind$ entries. There was a mismatch because of an index entry with the no-segment. This is the feature introduced by oracle to test the index selectivity without creating an index. Following is the test case for the same – &lt;br /&gt;&lt;br /&gt;Create a partitioned table&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; create table partt (n number, dt date, v varchar2(20)) partition by range (dt)&lt;br /&gt;  2  (&lt;br /&gt;  3  partition p1 values less than (to_date('01-JAN-2009','DD-MON-YYYY')),&lt;br /&gt;  4  partition p2 values less than (to_date('02-JAN-2009','DD-MON-YYYY')),&lt;br /&gt;  5  partition p3 values less than (to_date('03-JAN-2009','DD-MON-YYYY'))&lt;br /&gt;  6  );&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br&gt;&lt;br /&gt;&lt;br /&gt;Create one normal index and one nosegment index.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; create index indpart on partt(n) local;&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create index indpartx on partt(v) local nosegment;&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note that, virtual index entry cannot be found in dba_indexes but is available in dba_ind_columns.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select owner, index_name, table_name from dba_indexes where table_name='PARTT';&lt;br /&gt;&lt;br /&gt;OWNER      INDEX_NAME                     TABLE_NAME&lt;br /&gt;---------- ------------------------------ ------------------------------&lt;br /&gt;SYS        INDPART                        PARTT&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; select index_owner, index_name, table_owner, table_name, column_name&lt;br /&gt;  2  from dba_ind_columns where table_name='PARTT';&lt;br /&gt;&lt;br /&gt;INDEX_OWNER  INDEX_NAME   TABLE_OWNER  TABLE_NAME   COLUMN_NAME&lt;br /&gt;------------ ------------ ------------ ------------ ------------&lt;br /&gt;SYS          INDPARTX     SYS          PARTT        V&lt;br /&gt;SYS          INDPART      SYS          PARTT        N&lt;br /&gt;&lt;br /&gt;2 rows selected.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now, when we try to drop one of the partitions, we will get the error.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; alter table partt drop partition p1;&lt;br /&gt;alter table partt drop partition p1&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-00600: internal error code, arguments: [ktadrprc-1], [], [], [], [], [], [], []&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt; Solution:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To resolve this error, when we drop the virtual index, the partition got dropped.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; drop index indpartx;&lt;br /&gt;&lt;br /&gt;Index dropped.&lt;br /&gt;&lt;br /&gt;SQL&gt; alter table partt drop partition p1;&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Note : &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The solution given above is valid for 10gR2 (tested) version. For previous versions (Oracle 8i and 9i), even dropping an index will give following ORA-600 error. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; drop index indpartx;&lt;br /&gt;&lt;br /&gt;drop index indpartx&lt;br /&gt;           *&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-00600: internal error code, arguments: [4823], [], [], [], [], [], [], []&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Whereas in 8.1.7.4 version while dropping partition, you may get following error –&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; alter table partt drop partition p1;&lt;br /&gt;alter table partt drop partition p1&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-00600: internal error code, arguments: [4610], [], [], [], [], [], [], []&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Be aware of the creating virtual indexes on partitioned tables just for testing purpose. If it is very big production table, you may end up with unwanted troubles.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-6373441107357559495?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/ySYhvP22S7Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/6373441107357559495/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=6373441107357559495&amp;isPopup=true" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/6373441107357559495?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/6373441107357559495?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/ySYhvP22S7Q/ora-600-ktadrprc-1.html" title="ORA-600 [ktadrprc-1]" /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2009/06/ora-600-ktadrprc-1.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0ANQ3c_eCp7ImA9WxVRGUU.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-675579672093281055</id><published>2009-01-26T20:16:00.003+05:30</published><updated>2009-01-26T20:26:32.940+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-01-26T20:26:32.940+05:30</app:edited><title>Missing Tables in Execution Plan</title><content type="html">In Oracle 10g, one of the new features introduced for optimizer, named transformation. Using this features it may be possible that oracle internally removes some of the redundant tables those were involved only in join predicates. &lt;br /&gt;&lt;br /&gt;To demonstrate the same following test case created – &lt;br /&gt;    1. Table with primary key.&lt;br /&gt;    2. Table with foreign key referencing the above created table.&lt;br /&gt;    3. Query that eliminates the primary key table and hence leads to faster execution.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create table pk_table (id number, name varchar2(20));&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; alter table pk_table add constraint pk_id primary key(id);&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; create table fk_table (id number, phone number, addr varchar2(200));&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; alter table fk_table add constraint fk_id foreign key (id) references pk_table (id);&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2  for x in 1..100 loop&lt;br /&gt;  3  insert into pk_table values (x, dbms_random.string('A',20));&lt;br /&gt;  4  end loop;&lt;br /&gt;  5  commit;&lt;br /&gt;  6* end;&lt;br /&gt;SQL&gt; /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2  for x in 1..500 loop&lt;br /&gt;  3  insert into fk_table values (round(dbms_random.value(1,100)), dbms_random.value(1,100000), dbms_random.string('A',100));&lt;br /&gt;  4  end loop;&lt;br /&gt;  5  commit;&lt;br /&gt;  6  end;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats (user,'PK_TABLE', cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats (user,'FK_TABLE', cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select b.id, b.phone, b.addr&lt;br /&gt;  2  from pk_table a,&lt;br /&gt;  3       fk_table b&lt;br /&gt;  4  where a.id=b.id;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; @?/rdbms/admin/utlxpls&lt;br /&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;Plan hash value: 1108179040&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT  |          |   500 | 62500 |     5   (0)| 00:00:01 |&lt;br /&gt;|*  1 |  TABLE ACCESS FULL| FK_TABLE |   500 | 62500 |     5   (0)| 00:00:01 |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - filter("B"."ID" IS NOT NULL)&lt;br /&gt;&lt;br /&gt;13 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As shown above, the primary key table is missing in the execution plan. The table PK_TABLE is redundant. As all of us know, every record of foreign key column must be present in primary key table. Hence to fetch data from foreign key table we need have to check primary table for that record.   &lt;br /&gt;&lt;br /&gt;Optimizer added one extra filter condition i.e. “B.ID is not null”. This is to ensure the data consistency when transformation takes place. &lt;br /&gt;&lt;br /&gt;Starting from Oracle 11g, the optimizer has advanced to eliminate anti joins as well. For e.g. check the plan for following query in 10g and 11g.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Query&lt;/strong&gt;&lt;br /&gt;select * from fk_table b where not exists (select 1 from pk_table a where a.id=b.id);&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Plan in 10g&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;---------------------------------------------------------------------------------&lt;br /&gt;Plan hash value: 2976039779&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation          | Name     | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;-------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT   |          |     1 |   128 |     5   (0)| 00:00:01 |&lt;br /&gt;|   1 |  NESTED LOOPS ANTI |          |     1 |   128 |     5   (0)| 00:00:01 |&lt;br /&gt;|   2 |   TABLE ACCESS FULL| FK_TABLE |   500 | 62500 |     5   (0)| 00:00:01 |&lt;br /&gt;|*  3 |   INDEX UNIQUE SCAN| PK_ID    |   100 |   300 |     0   (0)| 00:00:01 |&lt;br /&gt;-------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   3 - access("A"."ID"="B"."ID")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;strong&gt;Plan in 11g&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;-------------------------------------------------------------------------------&lt;br /&gt;Plan hash value: 1108179040&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT  |          |     1 |   125 |     5   (0)| 00:00:01 |&lt;br /&gt;|*  1 |  TABLE ACCESS FULL| FK_TABLE |     1 |   125 |     5   (0)| 00:00:01 |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - filter("B"."ID" IS NULL)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This elimination is applicable to view as well. That means, if my view is fetching all columns from primary and foreign key tables and query is written to access only foreign key columns from this view, then also optimizer will eliminate primary table.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Limitations &lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;As this seems to be very interesting changes in optimizer, it is not applicable for the multi-column primary/foreign key columns. &lt;br /&gt;&lt;br /&gt;To sum up, now optimizer itself takes care of such unnecessary joins to eliminate table access to improve the query performance.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-675579672093281055?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/Dj1AuhnfTkA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/675579672093281055/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=675579672093281055&amp;isPopup=true" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/675579672093281055?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/675579672093281055?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/Dj1AuhnfTkA/missing-tables-in-execution-plan.html" title="Missing Tables in Execution Plan" /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2009/01/missing-tables-in-execution-plan.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak8ASX85cSp7ImA9WxRSFEQ.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-1218871914656661182</id><published>2008-09-15T22:41:00.001+05:30</published><updated>2008-09-15T22:44:08.129+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-15T22:44:08.129+05:30</app:edited><title>Materialized view fast refresh and performance.</title><content type="html">Oracle uses materialized view to replicate data to remote database in distributed environment. Also it can be used for cache expensive queries in a data warehouse environment. Many of the times, an issue is reported for low performance in fast or incremental refresh. There could be many of the reasons, bugs participating performance issues. One of them is to create an index on snaptime$$ column of corresponding MLOG$ table, which helps to fasten the fast refresh. It is required when MLOG$ table size grows due to any of the reason. &lt;br /&gt;&lt;br /&gt;Recently when I was looking into one of such issue, it was found that following query was getting executed (internally by Oracle) in primary database. &lt;br /&gt;&lt;br /&gt;Query&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT /*+ */ "A2"."CO_ID","A2"."TMCODE","A2"."SPCODE","A2"."SNCODE","A2"."CS_SEQNO",&lt;br /&gt; "A2"."CS_CHANNEL_NUM","A2"."CS_STAT_CHNG","A2"."CS_ON_CBB","A2"."CS_DATE_BILLED",&lt;br /&gt; "A2"."CS_REQUEST","A2"."SN_CLASS","A2"."CS_OVW_SUBSCR","A2"."CS_SUBSCRIPT",&lt;br /&gt; "A2"."CS_OVW_ACCESS","A2"."CS_OVW_ACC_PRD","A2"."CS_OVW_ACC_FIRST","A2"."CS_ACCESS",&lt;br /&gt; "A2"."CS_PENDING_STATE","A2"."CS_CHANNEL_EXCL","A2"."CS_DIS_SUBSCR","A2"."CS_ADV_CHARGE",&lt;br /&gt; "A2"."CS_SRV_TYPE","A2"."SUBPAYER","A2"."USGPAYER","A2"."ACCPAYER","A2"."CS_ENTDATE",&lt;br /&gt; "A2"."CS_OVW_LAST","A2"."INSTALL_DATE","A2"."TRIAL_END_DATE","A2"."CS_ADV_CHARGE_END_DATE",&lt;br /&gt; "A2"."PRM_VALUE_ID","A2"."CURRENCY","A2"."CS_ADV_CHARGE_CURRENCY",&lt;br /&gt;  "A2"."REC_VERSION","A2"."SRV_SUBTYPE" &lt;br /&gt;FROM  "CONTR_SERVICES" "A2", &lt;br /&gt; (SELECT DISTINCT "A3"."CO_ID" "CO_ID","A3"."SNCODE" "SNCODE","A3"."CS_SEQNO" "CS_SEQNO" &lt;br /&gt; FROM  "SYSADM"."MLOG$_CONTR_SERVICES" "A3" &lt;br /&gt; WHERE  "A3"."SNAPTIME$$"&gt;:1 &lt;br /&gt; AND  "A3"."DMLTYPE$$"&lt;&gt;'D') "A1" &lt;br /&gt;WHERE  "A2"."CO_ID"="A1"."CO_ID" &lt;br /&gt;AND  "A2"."SNCODE"="A1"."SNCODE" &lt;br /&gt;AND  "A2"."CS_SEQNO"="A1"."CS_SEQNO";&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Plan Table&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| Operation                 |  Name         |  Rows | Bytes|  Cost  | Pstart| Pstop |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| SELECT STATEMENT          |               |   748K|   87M|1382913 |       |       |&lt;br /&gt;|  HASH JOIN                |               |   748K|   87M|1382913 |       |       |&lt;br /&gt;|   VIEW                    |               |   751K|   27M|   3585 |       |       |&lt;br /&gt;|    SORT UNIQUE            |               |   751K|   16M|   3585 |       |       |&lt;br /&gt;|     TABLE ACCESS BY INDEX |MLOG$_CONTR_SER|   751K|   16M|     16 |       |       |&lt;br /&gt;|      INDEX RANGE SCAN     |MLOGSNAPTIME   |   751K|      |    655 |       |       |&lt;br /&gt;|   TABLE ACCESS FULL       |CONTR_SERVICES |   125M|    9G| 332467 |       |       |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Though there was an index available on SNAPTIME$$ table, the refresh was taking time because the base table size was huge (around 17GB) and it was doing FTS on it. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select owner, segment_name, bytes/1024/1024 "SIZE" from dba_segments&lt;br /&gt;  2  where segment_name='CONTR_SERVICES';&lt;br /&gt;&lt;br /&gt;OWNER      SEGMENT_NAME                         SIZE&lt;br /&gt;---------- ------------------------------ ----------&lt;br /&gt;SYSADM     CONTR_SERVICES                 17216.0156&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The mlog$ table size was also very big (around 750 MB) and having millions of records. Oracle opted for FTS because of table statistics available on MLOG$_CONTR_SERVICES table. Following are the available stats – &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select owner, table_name, partitioned, num_rows, blocks, global_stats&lt;br /&gt;  2  from dba_tables where table_name='MLOG$_CONTR_SERVICES';&lt;br /&gt;&lt;br /&gt;OWNER        TABLE_NAME                     PAR        NUM_ROWS     BLOCKS GLO&lt;br /&gt;------------ ------------------------------ --- --------------- ---------- ---&lt;br /&gt;SYSADM       MLOG$_CONTR_SERVICES           NO       22,531,900      95364 YES&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Most of the cases, fast refresh materialized view refresh is based on primary key of the base table. If regular fast refresh is happening, the number of rows gets replicated are very less and ideally it should do index scan with primary key. In the above mentioned case, few thousands rows were suppose to replicate. &lt;br /&gt;&lt;br /&gt;The customer was having dynamic analyze script wherein they were also analyzing mlog$ tables. To have better performance in fast refresh, we need to purge the mlog$ table regularly and stats on this table must be gathered when the table is empty. To prove this point, I have set customized stats on mlog table.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.set_table_stats ('SYSADM','MLOG$_CONTR_SERVICES',NUMROWS=&gt;0, NUMBLKS=&gt;0);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;SQL&gt; select owner, table_name, partitioned, num_rows, blocks, global_stats&lt;br /&gt;  2  from dba_tables where table_name='MLOG$_CONTR_SERVICES';&lt;br /&gt;&lt;br /&gt;OWNER        TABLE_NAME                     PAR        NUM_ROWS     BLOCKS GLO&lt;br /&gt;------------ ------------------------------ --- --------------- ---------- ---&lt;br /&gt;SYSADM       MLOG$_CONTR_SERVICES           NO                0          0 YES&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After setting above stats, the plan got changed.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Plan Table&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| Operation                 |  Name         |  Rows | Bytes|  Cost  | Pstart| Pstop |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| SELECT STATEMENT          |               |     1 |  122 |     27 |       |       |&lt;br /&gt;|  NESTED LOOPS             |               |     1 |  122 |     27 |       |       |&lt;br /&gt;|   VIEW                    |               |     1 |   39 |     24 |       |       |&lt;br /&gt;|    SORT UNIQUE            |               |     1 |   23 |     24 |       |       |&lt;br /&gt;|     TABLE ACCESS BY INDEX |MLOG$_CONTR_SER|     1 |   23 |      1 |       |       |&lt;br /&gt;|      INDEX RANGE SCAN     |MLOGSNAPTIME   |     1 |      |    655 |       |       |&lt;br /&gt;|   TABLE ACCESS BY INDEX RO|CONTR_SERVICES |   125M|    9G|      3 |       |       |&lt;br /&gt;|    INDEX UNIQUE SCAN      |PKCONTR_SERVICE|   125M|      |      2 |       |       |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In this case, it was doing proper index scan on primary key and fast refresh get successfully completed within minutes.&lt;br /&gt;&lt;br /&gt;To sum up, statistics play an important role and must have to be used in proper way. In materialized view refresh following things needs to be remembered&lt;br /&gt;&lt;br /&gt;1.  The MLOG$ table must be analyze when table is empty. &lt;br /&gt;2.  Regular MLOG$ purging is required, where size grows.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-1218871914656661182?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/t6v2bFy7nUU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/1218871914656661182/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=1218871914656661182&amp;isPopup=true" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/1218871914656661182?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/1218871914656661182?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/t6v2bFy7nUU/materialized-view-fast-refresh-and.html" title="Materialized view fast refresh and performance." /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2008/09/materialized-view-fast-refresh-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcBQ3w6cSp7ImA9WxdUEUg.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-3479233722476669344</id><published>2008-07-27T16:35:00.005+05:30</published><updated>2008-07-27T16:57:32.219+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-07-27T16:57:32.219+05:30</app:edited><title>Do we need stats on all columns?</title><content type="html">Recently I had visited one of the customers for performance review. As per the current stats gathering policy, they use following method to gather stats on the tables.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;dbms_stats.gather_table_stats (user, tabname, cascade=&gt;true, method_opt=&gt;'FOR ALL INDEXED COLUMNS 75');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As can be seen from the command, the input from method_opt is to gather stats on all the indexed columns with bucket size as 75. This means the stats on the other non-index columns will not be collected. This is a wrong practice and stats on the columns of the table are crucial, particularly if these are use in queries. &lt;br /&gt;&lt;br /&gt;When I was discussed with the customer, the customer had following misconception.&lt;br /&gt;&lt;br /&gt;   1. To make decision for index scan; the stats on indexed columns are available.&lt;br /&gt;   2. When there is no index available on column, why oracle needs stats on it. It will use FTS for such queries.&lt;br /&gt;&lt;br /&gt;This is purely a misconception, even if a column, use in a query, is not indexed; the correct cardinality is very crucial in determining a correct join order. To prove this point, I have created following small test case.&lt;br /&gt;&lt;br /&gt;I created two tables (test1 and test2) and indexes on it.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create table test1 as&lt;br /&gt;  2  select mod(LVL, 3)  ID, LVL VAL from&lt;br /&gt;  3  (select level "LVL" from dual connect by level &lt;= 100000);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create index ind_test1 on test1 (id);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create table test2 as&lt;br /&gt;  2  select lvl "ID", dbms_random.string('A',10) "STR" from (select&lt;br /&gt;  3  level "LVL" from dual connect by level &lt;=50000);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create index ind_test2 on test2 (id);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let us generate optimizer statistics based on the policy as the customer used.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats (user, 'TEST1', cascade=&gt;true, method_opt=&gt;'FOR ALL INDEXED COLUMNS SIZE 75');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats (user, 'TEST2', cascade=&gt;true, method_opt=&gt;'FOR ALL INDEXED COLUMNS SIZE 75');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The stats on the table are&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select owner, table_name, partitioned, num_rows, blocks&lt;br /&gt;  2  from dba_tables&lt;br /&gt;  3  where table_name in ('TEST1','TEST2')&lt;br /&gt;  4  and   owner = 'TEST';&lt;br /&gt;&lt;br /&gt;OWNER      TABLE_NAME PAR   NUM_ROWS     BLOCKS&lt;br /&gt;---------- ---------- --- ---------- ----------&lt;br /&gt;TEST       TEST1      NO      100000        186&lt;br /&gt;&lt;br /&gt;TEST       TEST2      NO       50000        156&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;break on table_name skip 1&lt;br /&gt;select table_name, column_name,num_distinct, num_nulls, density&lt;br /&gt;from dba_tab_columns&lt;br /&gt;where table_name in ('TEST1','TEST2')&lt;br /&gt;and   owner='TEST'&lt;br /&gt;order by table_name;&lt;br /&gt;&lt;br /&gt;TABLE_NAME COLUMN_NAM NUM_DISTINCT  NUM_NULLS    DENSITY&lt;br /&gt;---------- ---------- ------------ ---------- ----------&lt;br /&gt;TEST1      ID                    3          0    .000005&lt;br /&gt;           VAL             &lt;br /&gt;&lt;br /&gt;TEST2      ID                50000          0     .00002&lt;br /&gt;           STR               &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Based on the input to dbms_stats, stats are collected only on indexed columns and not for other non-indexed columns.&lt;br /&gt;The following query is used to check the execution plan.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; set autot trace&lt;br /&gt;SQL&gt; select a.id, a.val, b.str&lt;br /&gt;  2  from    test1 a,&lt;br /&gt;  3          test2 b&lt;br /&gt;  4  where   a.id = b.id&lt;br /&gt;  5  and     a.val = 40;&lt;br /&gt;&lt;br /&gt;Elapsed: 00:00:00.02&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=46 Card=667 Bytes=77372)&lt;br /&gt;   1    0   HASH JOIN (Cost=46 Card=667 Bytes=77372)&lt;br /&gt;   2    1     TABLE ACCESS (FULL) OF 'TEST1' (Cost=19 Card=1000 Bytes=16000)&lt;br /&gt;   3    1     TABLE ACCESS (FULL) OF 'TEST2' (Cost=17 Card=50000 Bytes=5000000)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          0  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;        327  consistent gets&lt;br /&gt;        315  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;        489  bytes sent via SQL*Net to client&lt;br /&gt;        499  bytes received via SQL*Net from client&lt;br /&gt;          2  SQL*Net roundtrips to/from client&lt;br /&gt;          0  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;          1  rows processed&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, let’s delete and gather new statistics on the same tables.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.delete_table_stats (user, 'TEST1');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.delete_table_stats (user, 'TEST2');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats (user, 'TEST1', cascade=&gt;true, method_opt=&gt;'FOR ALL COLUMNS');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats (user, 'TEST2', cascade=&gt;true, method_opt=&gt;'FOR ALL COLUMNS');&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let us check the stats again.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;TABLE_NAME COLUMN_NAM NUM_DISTINCT  NUM_NULLS    DENSITY&lt;br /&gt;---------- ---------- ------------ ---------- ----------&lt;br /&gt;TEST1      ID                    3          0    .000005&lt;br /&gt;           VAL              100000          0     .00001&lt;br /&gt;&lt;br /&gt;TEST2      ID                50000          0     .00002&lt;br /&gt;           STR               50000          0     .00002&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The plan by the same query is&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select a.id, a.val, b.str&lt;br /&gt;  2  from    test1 a,&lt;br /&gt;  3          test2 b&lt;br /&gt;  4  where   a.id = b.id&lt;br /&gt;  5  and     a.val = 40;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=21 Card=1 Bytes=22)&lt;br /&gt;   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST2' (Cost=2 Card=1 Bytes=15)&lt;br /&gt;   2    1     NESTED LOOPS (Cost=21 Card=1 Bytes=22)&lt;br /&gt;   3    2       TABLE ACCESS (FULL) OF 'TEST1' (Cost=19 Card=1 Bytes=7)&lt;br /&gt;   4    2       INDEX (RANGE SCAN) OF 'IND_TEST2' (NON-UNIQUE) (Cost=1 Card=1)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          0  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;        183  consistent gets&lt;br /&gt;        174  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;        489  bytes sent via SQL*Net to client&lt;br /&gt;        499  bytes received via SQL*Net from client&lt;br /&gt;          2  SQL*Net roundtrips to/from client&lt;br /&gt;          0  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;          1  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The comparison between both of the above plans is - &lt;br /&gt;&lt;table border=1 cellpadding=2 cellspacing=1&gt;&lt;br /&gt;&lt;th&gt;Plan when stats are gathered &lt;br /&gt;&lt;th&gt;Cardinality of TEST1 table&lt;br /&gt;&lt;th&gt;Consistent Gets&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Only on indexed columns &lt;td&gt;1000 &lt;td&gt;327&lt;br /&gt;&lt;tr&gt;&lt;td&gt;On all columns&lt;td&gt; 1 &lt;td&gt; 183&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Why such difference in both the execution plans?&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Cardinality plays a very big role in opting an execution plan. In first case, as the stats are not available on VAL column of the table TEST1. &lt;br /&gt;&lt;br /&gt;The cardinality when statistics are available only on indexed columns &lt;br /&gt;&lt;br /&gt;The filter condition is on ID column of table TEST1 and as there are no stats available on this column, oracle will take the default selectivity as 1/100 and calculated cardinality as &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;num_rows*selectivity = 100000*(1/100) = 1000&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As per the plan, the TEST1 is driving table and cardinality is very high and hence oracle opted the FTS for TEST2 table and cardinality is calculated as&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;num_rows = 50000&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When stats are available on both the all column of the tables, for TEST1 table cardinality is calculated as &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Num_rows*(“ID column selectivity” * “VAL column Selectivity)&lt;br /&gt;= round (100000 * (1/3 * 1/100000))&lt;br /&gt;= 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As cardinality cannot be 0 it will consider as 1.&lt;br /&gt;&lt;br /&gt;As the cardinality for TEST1 table is very low i.e. 1, TEST2 will be opted for index scan and hence cardinality is calculated as&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Num_rows * (1/distinct) &lt;br /&gt;= round (50000 * (1/50000)) &lt;br /&gt;= 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;From above discussion it is concluded that the stats are necessary on all columns. In the above-mentioned test case, we have calculated the stats with histograms but the same result can be found without histograms, as the column stats are getting updated. &lt;br /&gt;&lt;br /&gt;To sum up, the stats on all columns are required for optimal execution plan.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-3479233722476669344?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/Xbd8dmkav7k" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/3479233722476669344/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=3479233722476669344&amp;isPopup=true" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/3479233722476669344?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/3479233722476669344?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/Xbd8dmkav7k/do-we-need-stats-on-all-columns.html" title="Do we need stats on all columns?" /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2008/07/do-we-need-stats-on-all-columns.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkINSXcyeCp7ImA9WxdSFEs.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-8272923484268047168</id><published>2008-05-18T13:00:00.009+05:30</published><updated>2008-05-22T20:33:18.990+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-22T20:33:18.990+05:30</app:edited><title>Functions and Performance.</title><content type="html">Function plays an important role in development environment. The functions can be implicit (defined by oracle) or explicit (defined by user – PL/SQL code). Developers use the PL/SQL functions as they simplify the large code. &lt;br /&gt;&lt;br /&gt;The function may accept the value and must return the value. The user-defined functions are easy to use but they may degrade the performance badly. Hence it is always recommended to use the functions only and only when they are unavoidable. I have demonstrated the performance benefits by the simple query over user-defined functions. &lt;br /&gt;&lt;br /&gt;If the condition does not satisfy, function will return null value. It means the output rows will be the number of rows satisfied by the main query. For e.g. if suppose the main query returns 1000 rows and function satisfies only 400 rows, then the output will have 1000 rows, wherein the function column will show 600 null values and 400 value returned by the function. Hence we must have to use outer join whenever we are merging function into the simple query. The example below uses select sub-query that plays the role of function. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create table fn_tables as select * from dba_tables;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create table fn_indexes as select * from dba_indexes;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create index fn_ind_tables on fn_tables (table_name);&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats(user, 'FN_TABLES', cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec dbms_stats.gather_table_stats(user, 'FN_INDEXES', cascade=&gt;true);&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace function fn_test (tabowner varchar2, tabname varchar2)&lt;br /&gt;  2     return varchar2 is&lt;br /&gt;  3  tbsname varchar2(200);&lt;br /&gt;  4  begin&lt;br /&gt;  5     select tablespace_name into tbsname&lt;br /&gt;  6     from    fn_tables&lt;br /&gt;  7     where   owner=tabowner&lt;br /&gt;  8     and     table_name=tabname;&lt;br /&gt;  9  return tbsname;&lt;br /&gt; 10  end;&lt;br /&gt; 11  /&lt;br /&gt;&lt;br /&gt;Function created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; set autot trace&lt;br /&gt;SQL&gt; select a.owner, a.index_name, a.status,&lt;br /&gt;  2     fn_test(a.owner, a.table_name) "TBS_NAME"&lt;br /&gt;  3  from       fn_indexes a&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;1072 rows selected.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 2626245312&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name       | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT  |            |  1072 | 51456 |     9   (0)| 00:00:01 |&lt;br /&gt;|   1 |  TABLE ACCESS FULL| FN_INDEXES |  1072 | 51456 |     9   (0)| 00:00:01 |&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;       1097  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;&lt;strong&gt;       3308  consistent gets&lt;/strong&gt;&lt;br /&gt;          0  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;      36706  bytes sent via SQL*Net to client&lt;br /&gt;       1162  bytes received via SQL*Net from client&lt;br /&gt;         73  SQL*Net roundtrips to/from client&lt;br /&gt;          0  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;       1072  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select a.owner, a.index_name, a.status,&lt;br /&gt;  2        (select tablespace_name from fn_tables b&lt;br /&gt;  3         where b.owner=a.owner&lt;br /&gt;  4         and   a.table_name=b.table_name) "TBS_NAME"&lt;br /&gt;  5  from  fn_indexes a&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;1072 rows selected.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 321380953&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation                   | Name         | Rows  | Bytes | Cost| Time     |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT            |              |  1072 | 51456 |    9| 00:00:01 |&lt;br /&gt;|*  1 |  TABLE ACCESS BY INDEX ROWID| FN_TABLES    |     1 |    29 |    2| 00:00:01 |&lt;br /&gt;|*  2 |   INDEX RANGE SCAN          | FN_IND_TABLES|     1 |       |    1| 00:00:01 |&lt;br /&gt;|   3 |  TABLE ACCESS FULL          | FN_INDEXES   |  1072 | 51456 |    9| 00:00:01 |&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - filter("B"."OWNER"=:B1)&lt;br /&gt;   2 - access("B"."TABLE_NAME"=:B1)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          0  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;&lt;strong&gt;       1573  consistent gets&lt;/strong&gt;&lt;br /&gt;          0  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;      36706  bytes sent via SQL*Net to client&lt;br /&gt;       1162  bytes received via SQL*Net from client&lt;br /&gt;         73  SQL*Net roundtrips to/from client&lt;br /&gt;          0  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;       1072  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select a.owner, a.index_name, a.status, b.tablespace_name "TBS_NAME"&lt;br /&gt;  2  from   fn_indexes a,&lt;br /&gt;  3         fn_tables b&lt;br /&gt;  4  where  b.owner(+) = a.owner&lt;br /&gt;  5  and    b.table_name(+) = a.table_name&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;1072 rows selected.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Execution Plan&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;Plan hash value: 893717710&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation             | Name       | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT      |            |  1072 | 82544 |    18   (6)| 00:00:01 |&lt;br /&gt;|*  1 |  HASH JOIN RIGHT OUTER|            |  1072 | 82544 |    18   (6)| 00:00:01 |&lt;br /&gt;|   2 |   TABLE ACCESS FULL   | FN_TABLES  |   879 | 25491 |     8   (0)| 00:00:01 |&lt;br /&gt;|   3 |   TABLE ACCESS FULL   | FN_INDEXES |  1072 | 51456 |     9   (0)| 00:00:01 |&lt;br /&gt;------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - access("B"."OWNER"(+)="A"."OWNER" AND&lt;br /&gt;              "B"."TABLE_NAME"(+)="A"."TABLE_NAME")&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Statistics&lt;br /&gt;----------------------------------------------------------&lt;br /&gt;          0  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;&lt;strong&gt;        135  consistent gets&lt;/strong&gt;&lt;br /&gt;          0  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;      36706  bytes sent via SQL*Net to client&lt;br /&gt;       1162  bytes received via SQL*Net from client&lt;br /&gt;         73  SQL*Net roundtrips to/from client&lt;br /&gt;          0  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;       1072  rows processed&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Place all above required values in tabular format.&lt;br /&gt;&lt;br /&gt;&lt;table border=1 cellpadding=2 cellspacing=1&gt;&lt;br /&gt;&lt;th&gt;Query No.&lt;th&gt; Query Criteria &lt;th&gt; Cost &lt;th&gt; Recursive Calls &lt;th&gt; Consistent Gets&lt;br /&gt;&lt;tr&gt;&lt;td&gt; 1 &lt;td&gt; With Function &lt;td&gt; 9 &lt;td&gt; 1097 &lt;td&gt; 3308&lt;br /&gt;&lt;tr&gt;&lt;td&gt; 2 &lt;td&gt; With Sub-Query &lt;td&gt; 9 &lt;td&gt; 0 &lt;td&gt; 1573&lt;br /&gt;&lt;tr&gt;&lt;td&gt; 3 &lt;td&gt; With Simple Query &lt;td&gt; 18 &lt;td&gt; 0 &lt;td&gt; 135&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;As shown in above table, though the cost of the third query is high, the number of consistent gets has been reduced tremendously. When we are using function, the recursive calls and consistent gets are very high. But when using sub-query both the values have been reduced but this query also suffers from performance compared to third query. &lt;br /&gt;&lt;br /&gt;Recently I had got the following query from one of our customer. Due to confidentiality, I will not be posting the original query from the customer site but in this example I have made similar kind of query.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;create or replace function get_minamount (cust number) &lt;br /&gt;return number is&lt;br /&gt;amt number;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;     select min (amount) into amt &lt;br /&gt;     from contract &lt;br /&gt;     where customer=cust and status = 'a';&lt;br /&gt;&lt;br /&gt;     if (amt is null) then&lt;br /&gt;        select min (amount) into amt &lt;br /&gt;        from   contract &lt;br /&gt;        where  customer=cust and status = 'd';&lt;br /&gt;     end if;&lt;br /&gt;&lt;br /&gt;     if (amt is null) then&lt;br /&gt;        select min (amount) into amt &lt;br /&gt;        from   contract &lt;br /&gt;        where  customer=cust and status = 's';&lt;br /&gt;     end if;&lt;br /&gt;&lt;br /&gt;     Return amt;&lt;br /&gt;&lt;br /&gt;End;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The main query is as follows.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;select customer, name, address, &lt;br /&gt;       get_minamount (customer) "MIN_AMT"&lt;br /&gt;from   customer_master &lt;br /&gt;where  custcode is like 'Large%';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The customer_master is having millions of records and to execute this query it was taking around 8 Hrs. The given suggestion on this query is as follows. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;select customer, name, address, min_amt, status&lt;br /&gt;from  (select a.customer, name, address, status, &lt;br /&gt;              min(case when status in ('a','s','d') then amount end) &lt;br /&gt;   over (partition by b.customer, b.status) "MIN_AMT", &lt;br /&gt;              rank() over (partition by b.customer &lt;br /&gt;              order by (case when status = 'a' and amount is not null then 1&lt;br /&gt;                             when status = 's' and amount is not null then 2&lt;br /&gt;                             when status = 'd' and amount is not null then 3 &lt;br /&gt;                       end), rownum) "RNK"&lt;br /&gt;       from   customer_master a, &lt;br /&gt;              contract b&lt;br /&gt;       where  a.customer = b.customer(+))&lt;br /&gt;where rnk = 1;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;After implementing the query the performance gain was very high. The modified query is taking around 20 minutes to execute against 8 Hrs. &lt;br /&gt;&lt;br /&gt;To sum up, the user-defined functions may contribute in performance degradation of the query.  The simple query will give major performance benefits against the query with functions. The performance of the query is inversely proportional to the number of consistent gets. Hence to improve the performance, we must minimize the consistent gets.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-8272923484268047168?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/AGbscSsrrlE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/8272923484268047168/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=8272923484268047168&amp;isPopup=true" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/8272923484268047168?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/8272923484268047168?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/AGbscSsrrlE/functions-and-performance_18.html" title="Functions and Performance." /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2008/05/functions-and-performance_18.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkUFRX46cSp7ImA9WxZaGEo.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-1109501243995271332</id><published>2008-05-04T10:15:00.003+05:30</published><updated>2008-05-04T10:46:54.019+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-04T10:46:54.019+05:30</app:edited><title>Migration from Filesystem to ASM Files.</title><content type="html">This is continuation to my previous blog, wherein we discussed about interacting with ASM and OS database file. &lt;br /&gt;&lt;br /&gt;ASM is an integrated volume manager and file system for Oracle Database files. It simplifies, automates storage management, increase storage utilization and delivers predictable performance. Due to this many of us wants to migrate filesystem database to ASM. If the database is very large and critical, we may not be able to afford the migration downtime. In this blog, I would discuss the procedure to dramitically reduce downtime during the process of migration from raw/OS filesystem to ASM using Oracle Data Guard and RMAN. This blog will also be helpful to create standby database. This procedure explains how data guard can help to reduce the downtime of migration. &lt;br /&gt;&lt;br /&gt;Following is the summarised procedure&lt;br /&gt;&lt;br /&gt;1.  Create standby (dataguard) of the production database.&lt;br /&gt;2.  Migrate the standby database to ASM&lt;br /&gt;3.  Do the switchover to migate the database to ASM. &lt;br /&gt;&lt;br /&gt;As mentioned above, it seems, migration is very simple and yes it is. Below mentioned is the detailed procedure.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;PREPARE FOR STANDBY DATABASE&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;In this demonstration, I have named primary instance as “ORA10G” and standby database as “ORA10GSTD”&lt;br /&gt;&lt;br /&gt;Create a backup of the database using RMAN. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;RMAN&gt; connect target /&lt;br /&gt;&lt;br /&gt;connected to target database: ORA10G (DBID=3970225046)&lt;br /&gt;&lt;br /&gt;RMAN&gt; backup database include current controlfile for standby;&lt;br /&gt;&lt;br /&gt;Starting backup at 08-APR-08&lt;br /&gt;using target database control file instead of recovery catalog&lt;br /&gt;allocated channel: ORA_DISK_1&lt;br /&gt;channel ORA_DISK_1: sid=154 devtype=DISK&lt;br /&gt;channel ORA_DISK_1: starting full datafile backupset&lt;br /&gt;channel ORA_DISK_1: specifying datafile(s) in backupset&lt;br /&gt;input datafile fno=00001 name=D:\ORACLE\DATAFILES\ORA10G\SYSTEM01.DBF&lt;br /&gt;input datafile fno=00002 name=D:\ORACLE\DATAFILES\ORA10G\UNDOTBS01.DBF&lt;br /&gt;input datafile fno=00003 name=D:\ORACLE\DATAFILES\ORA10G\SYSAUX01.DBF&lt;br /&gt;input datafile fno=00004 name=D:\ORACLE\DATAFILES\ORA10G\USERS01.DBF&lt;br /&gt;channel ORA_DISK_1: starting piece 1 at 08-APR-08&lt;br /&gt;channel ORA_DISK_1: finished piece 1 at 08-APR-08&lt;br /&gt;piece handle=D:\ORACLE\ORA10GHOME\DATABASE\01JDBCU5_1_1 tag=TAG20080408T230957 comment=NONE&lt;br /&gt;channel ORA_DISK_1: backup set complete, elapsed time: 00:00:56&lt;br /&gt;channel ORA_DISK_1: starting full datafile backupset&lt;br /&gt;channel ORA_DISK_1: specifying datafile(s) in backupset&lt;br /&gt;including standby control file in backupset&lt;br /&gt;including current SPFILE in backupset&lt;br /&gt;channel ORA_DISK_1: starting piece 1 at 08-APR-08&lt;br /&gt;channel ORA_DISK_1: finished piece 1 at 08-APR-08&lt;br /&gt;piece handle=D:\ORACLE\ORA10GHOME\DATABASE\02JDBCVT_1_1 tag=TAG20080408T230957 comment=NONE&lt;br /&gt;channel ORA_DISK_1: backup set complete, elapsed time: 00:00:05&lt;br /&gt;Finished backup at 08-APR-08&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Make the tnsnames.ora changes in both (production and standby) servers to make connections between both the servers for communication.&lt;br /&gt;&lt;br /&gt;Create and copy the parameter file and make appropriate changes for standby database.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create pfile from spfile;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We assume that, ASM instance is already created, mounted and ready for use. We are using diskgroup, named as ORADG. Also make sure this diskgroup should have sufficient space available.&lt;br /&gt;&lt;br /&gt;Following are the standby parameters, which we have to modify.&lt;br /&gt;&lt;br /&gt;Edit control_files parameter to appropriate location. This location should be ASM diskgroup. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;control_files = ‘+ORADG\ORA10g\controlfiles\control01.ctl’&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Add or edit DB_UNIQUE_NAME parameter to define the database uniqueness.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;db_unique_name = ORA10GSTD  ---- Instance name of Standby.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Add or edit LOG_ARCHIVE_CONFIG parameter to identify primary and standby database using unique name. The parameter value should be primary and standby instance name.&lt;br /&gt;&lt;pre&gt; &lt;br /&gt;log_archive_config = ’dg_config=(ORA10G,ORA10GSTD)’&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Set following parameters to point to ASM diskgroup. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;db_create_file_dest = '+ORADG'&lt;br /&gt;db_create_online_log_dest_1 = '+ORADG'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The other parameters that needs to change are – &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;instance_name = ORA10gSTD&lt;br /&gt;fal_server = ora10g     --- TNSNAMES entry for Primary&lt;br /&gt;fal_client = ora10gSTD  --- TNSNAMES entry for Standby&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Create Password file&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ orapwd file=$ORACLE_HOME/dbs/orapw&lt;SID&gt; password=&lt;SYS PWD&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;strong&gt;INITIATE THE STANDBY DATABASE IN ASM&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;After confirmation ASM instance up and parametter, password file is prepared, now we initiate standby database.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; startup force nomount&lt;br /&gt;ORACLE instance started.&lt;br /&gt;&lt;br /&gt;Total System Global Area  301989888 bytes&lt;br /&gt;Fixed Size                  1248672 bytes&lt;br /&gt;Variable Size              96469600 bytes&lt;br /&gt;Database Buffers          201326592 bytes&lt;br /&gt;Redo Buffers                2945024 bytes&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;RMAN provides single command that initiates the standby database using source database information. Connect to the production database using RMAN and initiate standby database.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;C:\&gt; rman target sys/oracle@ora10g&lt;br /&gt;&lt;br /&gt;Recovery Manager: Release 10.2.0.1.0 - Production &lt;br /&gt;&lt;br /&gt;Copyright (c) 1982, 2005, Oracle.  All rights reserved.&lt;br /&gt;&lt;br /&gt;connected to target database: ORA10G (DBID=3970225046)&lt;br /&gt;&lt;br /&gt;RMAN&gt; connect auxiliary /&lt;br /&gt;&lt;br /&gt;connected to auxiliary database: ORA10G (not mounted)&lt;br /&gt;&lt;br /&gt;RMAN&gt; duplicate target database for standby;&lt;br /&gt;&lt;br /&gt;Starting Duplicate Db at 08-APR-08&lt;br /&gt;using target database control file instead of recovery catalog&lt;br /&gt;allocated channel: ORA_AUX_DISK_1&lt;br /&gt;channel ORA_AUX_DISK_1: sid=157 devtype=DISK&lt;br /&gt;&lt;br /&gt;contents of Memory Script:&lt;br /&gt;{&lt;br /&gt;   restore clone standby controlfile;&lt;br /&gt;   sql clone 'alter database mount standby database';&lt;br /&gt;}&lt;br /&gt;executing Memory Script&lt;br /&gt;&lt;br /&gt;Starting restore at 08-APR-08&lt;br /&gt;using channel ORA_AUX_DISK_1&lt;br /&gt;&lt;br /&gt;channel ORA_AUX_DISK_1: starting datafile backupset restore&lt;br /&gt;channel ORA_AUX_DISK_1: restoring control file&lt;br /&gt;channel ORA_AUX_DISK_1: reading from backup piece D:\ORACLE\ORA10GHOME\DATABASE\02JDBCVT_1_1&lt;br /&gt;channel ORA_AUX_DISK_1: restored backup piece 1&lt;br /&gt;piece handle=D:\ORACLE\ORA10GHOME\DATABASE\02JDBCVT_1_1 tag=TAG20080408T230957&lt;br /&gt;channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:17&lt;br /&gt;output filename=+ORADG/ora10g/controlfiles/control01.ctl&lt;br /&gt;Finished restore at 08-APR-08&lt;br /&gt;&lt;br /&gt;sql statement: alter database mount standby database&lt;br /&gt;released channel: ORA_AUX_DISK_1&lt;br /&gt;&lt;br /&gt;contents of Memory Script:&lt;br /&gt;{&lt;br /&gt;   set newname for clone tempfile  1 to new;&lt;br /&gt;   switch clone tempfile all;&lt;br /&gt;   set newname for clone datafile  1 to new;&lt;br /&gt;   set newname for clone datafile  2 to new;&lt;br /&gt;   set newname for clone datafile  3 to new;&lt;br /&gt;   set newname for clone datafile  4 to new;&lt;br /&gt;   restore&lt;br /&gt;   check readonly&lt;br /&gt;   clone database&lt;br /&gt;   ;&lt;br /&gt;}&lt;br /&gt;executing Memory Script&lt;br /&gt;&lt;br /&gt;executing command: SET NEWNAME&lt;br /&gt;&lt;br /&gt;renamed temporary file 1 to +ORADG in control file&lt;br /&gt;&lt;br /&gt;executing command: SET NEWNAME&lt;br /&gt;&lt;br /&gt;executing command: SET NEWNAME&lt;br /&gt;&lt;br /&gt;executing command: SET NEWNAME&lt;br /&gt;&lt;br /&gt;executing command: SET NEWNAME&lt;br /&gt;&lt;br /&gt;Starting restore at 08-APR-08&lt;br /&gt;allocated channel: ORA_AUX_DISK_1&lt;br /&gt;channel ORA_AUX_DISK_1: sid=157 devtype=DISK&lt;br /&gt;&lt;br /&gt;channel ORA_AUX_DISK_1: starting datafile backupset restore&lt;br /&gt;channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set&lt;br /&gt;restoring datafile 00001 to +ORADG&lt;br /&gt;restoring datafile 00002 to +ORADG&lt;br /&gt;restoring datafile 00003 to +ORADG&lt;br /&gt;restoring datafile 00004 to +ORADG&lt;br /&gt;channel ORA_AUX_DISK_1: reading from backup piece D:\ORACLE\ORA10GHOME\DATABASE\01JDBCU5_1_1&lt;br /&gt;channel ORA_AUX_DISK_1: restored backup piece 1&lt;br /&gt;piece handle=D:\ORACLE\ORA10GHOME\DATABASE\01JDBCU5_1_1 tag=TAG20080408T230957&lt;br /&gt;channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:01:46&lt;br /&gt;Finished restore at 08-APR-08&lt;br /&gt;&lt;br /&gt;contents of Memory Script:&lt;br /&gt;{&lt;br /&gt;   switch clone datafile all;&lt;br /&gt;}&lt;br /&gt;executing Memory Script&lt;br /&gt;&lt;br /&gt;datafile 1 switched to datafile copy&lt;br /&gt;input datafile copy recid=5 stamp=651539654 filename=+ORADG/ora10gstd/datafile/system.257.651539549&lt;br /&gt;datafile 2 switched to datafile copy&lt;br /&gt;input datafile copy recid=6 stamp=651539654 filename=+ORADG/ora10gstd/datafile/undotbs1.258.651539549&lt;br /&gt;datafile 3 switched to datafile copy&lt;br /&gt;input datafile copy recid=7 stamp=651539654 filename=+ORADG/ora10gstd/datafile/sysaux.259.651539551&lt;br /&gt;datafile 4 switched to datafile copy&lt;br /&gt;input datafile copy recid=8 stamp=651539654 filename=+ORADG/ora10gstd/datafile/users.260.651539551&lt;br /&gt;Finished Duplicate Db at 08-APR-08&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We have successfully transferred files from production to standby database in ASM datagroup. Modify log_archive_dest_2 parameter so that archive logs file will transfer from primary to standby database. Then place the standby database recovery mode so archive logs can be applied automatically.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; recover managed standby database disconnect;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;strong&gt;DO SWITCHOVER TO MIGRATE DATABASE TO ASM&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;This is final step of the migration. We will do switchover to migrate the database to ASM. Check that, both primary and standby must be in sync. &lt;br /&gt;&lt;br /&gt;On Primary Database – &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Select max(sequence#) “MAXSEQ” from v$log_history;&lt;br /&gt;&lt;br /&gt;MAXSEQ&lt;br /&gt;---------&lt;br /&gt;      124&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;On Standby Database – &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Select max(sequence#) “MAXSEQ” from v$log_history;&lt;br /&gt;&lt;br /&gt;MAXSEQ&lt;br /&gt;---------&lt;br /&gt;      124&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As both primary and standby database, we can do switchover. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; alter database commit to switchover to primary;&lt;br /&gt;&lt;br /&gt;Database altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; shut immediate;&lt;br /&gt;ORA-01109: database not open&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Database dismounted.&lt;br /&gt;ORACLE instance shut down.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; startup&lt;br /&gt;ORACLE instance started.&lt;br /&gt;&lt;br /&gt;Total System Global Area  301989888 bytes&lt;br /&gt;Fixed Size                  1248672 bytes&lt;br /&gt;Variable Size              96469600 bytes&lt;br /&gt;Database Buffers          201326592 bytes&lt;br /&gt;Redo Buffers                2945024 bytes&lt;br /&gt;Database mounted.&lt;br /&gt;Database opened.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We have completed ASM migration.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-1109501243995271332?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/TOQU5w7j3Eg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/1109501243995271332/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=1109501243995271332&amp;isPopup=true" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/1109501243995271332?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/1109501243995271332?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/TOQU5w7j3Eg/migration-from-filesystem-to-asm-files.html" title="Migration from Filesystem to ASM Files." /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2008/05/migration-from-filesystem-to-asm-files.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8ARns8fyp7ImA9WxZbF00.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-6978201667665325306</id><published>2008-04-20T19:14:00.012+05:30</published><updated>2008-04-20T21:40:47.577+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-04-20T21:40:47.577+05:30</app:edited><title>Manual Switchover.</title><content type="html">Data Guard maintains the standby databases as transactionally consistent copies of the production database. Then, if the production database becomes unavailable because of a planned or an unplanned outage, Data Guard can switch any standby database to the production role, minimizing the downtime associated with the outage.&lt;br /&gt;&lt;br /&gt;Due to low network bandwidth or when using Standard Edition, we maintain manual dataguard, in which, we transfer the archive log files manually to the DR site and then apply them there. In such cases, it is very difficult to use oracle internal switch over mechanism. This document is prepared for the customers having manual dataguard and wants to do the switch over.&lt;br /&gt;&lt;br /&gt;This document consider following pre-requisites.&lt;br /&gt;&lt;br /&gt; All Dataguard pre-requisites (same OS and Oracle version etc.) are fulfilled.&lt;br /&gt; Manual Dataguard setup is in place having standby database with standby controlfile.&lt;br /&gt; All the archive logs are properly applied on DR site.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Switch-over Process&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;1. Shutdown the primary database.&lt;br /&gt;2. Make sure all the archive logs are transferred and applied on DR site.&lt;br /&gt;3. Shutdown the standby database&lt;br /&gt;4. Copy the controlfile and online redo log files of primary database to DR site.&lt;br /&gt;5. Copy the standby controlfile from DR site to primary site.&lt;br /&gt;6. Startup the primary database (Current standby database). Here we may have to do recovery to apply changes of online redo log files. This must be a complete recovery.&lt;br /&gt;7. Startup the standby database (Current primary)&lt;br /&gt;8. Check the databases &amp;amp; their role.&lt;br /&gt;&lt;br /&gt;We have successfully completed the switchover activity and same steps can be followed for switch back activity.&lt;br /&gt;&lt;br /&gt;Following is the demonstration of the above-mentioned steps. We will create a table and check the availability of it after switchover.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Primary Database –&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL&gt; create table std_test_1 as select * from dba_objects;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into std_test_1 select * from std_test_1;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;6165 rows created.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into std_test_1 select * from std_test_1;&lt;br /&gt;&lt;br /&gt;12330 rows created.&lt;br /&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note down the current sequence number of primary database.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select max(sequence#) from v$log_history;&lt;br /&gt;&lt;br /&gt;MAX(SEQUENCE#)&lt;br /&gt;--------------&lt;br /&gt;           235&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we will check the status of standby database. (Its current sequence no.)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select max(sequence#) from v$log_history;&lt;br /&gt;&lt;br /&gt;MAX(SEQUENCE#)&lt;br /&gt;--------------&lt;br /&gt;           233&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The standby database is lagging of primary database. So we will recover the&lt;br /&gt;standby database and sync it with primary.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; recover standby database;&lt;br /&gt;ORA-00279: change 50652 generated at 04/02/2008 22:32:37 needed for thread 1&lt;br /&gt;ORA-00289: suggestion : D:\ORACLE\DATAFILES\ORA9ISTD\ARCH\ORA9I_234.DBF&lt;br /&gt;ORA-00280: change 50652 for thread 1 is in sequence #234&lt;br /&gt;&lt;br /&gt;Specify log: {&lt;ret&gt;=suggested filename AUTO CANCEL}&lt;br /&gt;ORA-00279: change 50750 generated at 04/02/2008 22:36:08 needed for thread 1&lt;br /&gt;ORA-00289: suggestion : D:\ORACLE\DATAFILES\ORA9ISTD\ARCH\ORA9I_235.DBF&lt;br /&gt;ORA-00280: change 50750 for thread 1 is in sequence #235&lt;br /&gt;ORA-00278: log file 'D:\ORACLE\DATAFILES\ORA9ISTD\ARCH\ORA9I_234.DBF' no longer needed for this recovery&lt;br /&gt;&lt;br /&gt;Specify log: {&lt;ret&gt;=suggested filename AUTO CANCEL}&lt;br /&gt;ORA-00279: change 50759 generated at 04/02/2008 22:36:14 needed for thread 1&lt;br /&gt;ORA-00289: suggestion : D:\ORACLE\DATAFILES\ORA9ISTD\ARCH\ORA9I_236.DBF&lt;br /&gt;ORA-00280: change 50759 for thread 1 is in sequence #236&lt;br /&gt;ORA-00278: log file 'D:\ORACLE\DATAFILES\ORA9ISTD\ARCH\ORA9I_235.DBF' no longer needed for this recovery&lt;br /&gt;&lt;br /&gt;Specify log: {&lt;ret&gt;=suggested filename AUTO CANCEL}&lt;br /&gt;ORA-00308: cannot open archived log&lt;br /&gt;'D:\ORACLE\DATAFILES\ORA9ISTD\ARCH\ORA9I_236.DBF'&lt;br /&gt;ORA-27041: unable to open file&lt;br /&gt;OSD-04002: unable to open file&lt;br /&gt;O/S-Error: (OS 2) The system cannot find the file specified.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Check the current status of standby database.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select max(sequence#) from v$log_history;&lt;br /&gt;&lt;br /&gt;MAX(SEQUENCE#)&lt;br /&gt;--------------&lt;br /&gt;           235&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we are ready to proceed for switchover process. To start with, we will first&lt;br /&gt;shutdown both primary and standby database.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Primary –&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; shut immediate;&lt;br /&gt;Database closed.&lt;br /&gt;Database dismounted.&lt;br /&gt;ORACLE instance shut down.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;strong&gt;Standby –&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; shut immediate;&lt;br /&gt;ORA-01109: database not open&lt;br /&gt;&lt;br /&gt;Database dismounted.&lt;br /&gt;ORACLE instance shut down.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We are doing manual switchover process, so we need to copy the control and&lt;br /&gt;redo log files from primary to standby database. At the same time, we have to&lt;br /&gt;copy the controlfile from standby database location to primary database.&lt;br /&gt;&lt;br /&gt;Assuming copy process is over, we will startup the Current Primary (former&lt;br /&gt;standby) database.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; startup nomount&lt;br /&gt;ORACLE instance started.&lt;br /&gt;&lt;br /&gt;Total System Global Area 101784276 bytes&lt;br /&gt;Fixed Size 453332 bytes&lt;br /&gt;Variable Size 75497472 bytes&lt;br /&gt;Database Buffers 25165824 bytes&lt;br /&gt;Redo Buffers 667648 bytes&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; alter database mount;&lt;br /&gt;&lt;br /&gt;Database altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; alter database open;&lt;br /&gt;&lt;br /&gt;alter database open&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-01113: file 1 needs media recovery&lt;br /&gt;ORA-01110: data file 1: 'D:\ORACLE\DATAFILES\ORA9ISTD\SYSTEM01.DBF'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here we are getting recovery error because this is standby database; the changes&lt;br /&gt;lying in current redo logs (which we have copied) are yet to apply on current&lt;br /&gt;primary. The important thing to note here is that, this recovery must be complete&lt;br /&gt;recovery. Post to this, we will open the check the status of the database.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; recover database;&lt;br /&gt;&lt;br /&gt;Media recovery complete.&lt;br /&gt;&lt;br /&gt;SQL&gt; alter database open;&lt;br /&gt;&lt;br /&gt;Database altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; select max(sequence#) from v$log_history;&lt;br /&gt;&lt;br /&gt;MAX(SEQUENCE#)&lt;br /&gt;--------------&lt;br /&gt;           235&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As shown above, the recovery has been completed and current primary (former&lt;br /&gt;standby) database is up. We will check for test table created.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; select count(1) from std_test_1;&lt;br /&gt;&lt;br /&gt;  COUNT(1)&lt;br /&gt;----------&lt;br /&gt;     24660&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We have successfully completed one phase of the switchover process. Now we&lt;br /&gt;will startup current standby (former primary) database to proceed further.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; startup nomount&lt;br /&gt;ORACLE instance started.&lt;br /&gt;&lt;br /&gt;Total System Global Area 101784276 bytes&lt;br /&gt;Fixed Size 453332 bytes&lt;br /&gt;Variable Size 75497472 bytes&lt;br /&gt;Database Buffers 25165824 bytes&lt;br /&gt;Redo Buffers 667648 bytes&lt;br /&gt;&lt;br /&gt;SQL&gt; alter database mount standby database;&lt;br /&gt;&lt;br /&gt;Database altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; select max(sequence#) from v$log_history;&lt;br /&gt;&lt;br /&gt;MAX(SEQUENCE#)&lt;br /&gt;--------------&lt;br /&gt;           235&lt;br /&gt;&lt;br /&gt;SQL&gt; select open_mode, database_role from v$database;&lt;br /&gt;&lt;br /&gt;OPEN_MODE DATABASE_ROLE&lt;br /&gt;---------- ----------------&lt;br /&gt;MOUNTED PHYSICAL STANDBY&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The primary and standby databases are in sync. We will generate the archive log&lt;br /&gt;files at primary site and will apply these files at standby database.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; alter system switch logfile;&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; alter system switch logfile;&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; alter system switch logfile;&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; select max(sequence#) from v$log_history;&lt;br /&gt;&lt;br /&gt;MAX(SEQUENCE#)&lt;br /&gt;--------------&lt;br /&gt;           238&lt;br /&gt;&lt;br /&gt;SQL&gt; select open_mode, database_role from v$database;&lt;br /&gt;&lt;br /&gt;OPEN_MODE DATABASE_ROLE&lt;br /&gt;---------- ----------------&lt;br /&gt;READ WRITE PRIMARY&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We have generated three archive log files here and now we will copy and apply&lt;br /&gt;them at standby database.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; recover standby database;&lt;br /&gt;ORA-00279: change 50759 generated at 04/02/2008 22:36:14 needed for thread 1&lt;br /&gt;ORA-00289: suggestion : D:\ORACLE\DATAFILES\ORA9I\ARCH\ORA9I_236.DBF&lt;br /&gt;ORA-00280: change 50759 for thread 1 is in sequence #236&lt;br /&gt;&lt;br /&gt;Specify log: {&lt;ret&gt;=suggested filename AUTO CANCEL}&lt;br /&gt;ORA-00279: change 51098 generated at 04/02/2008 22:49:37 needed for thread 1&lt;br /&gt;ORA-00289: suggestion : D:\ORACLE\DATAFILES\ORA9I\ARCH\ORA9I_237.DBF&lt;br /&gt;ORA-00280: change 51098 for thread 1 is in sequence #237&lt;br /&gt;ORA-00278: log file 'D:\ORACLE\DATAFILES\ORA9I\ARCH\ORA9I_236.DBF' no&lt;br /&gt;longer needed for this recovery&lt;br /&gt;&lt;br /&gt;Specify log: {&lt;ret&gt;=suggested filename AUTO CANCEL}&lt;br /&gt;ORA-00279: change 51119 generated at 04/02/2008 22:49:50 needed for thread 1&lt;br /&gt;ORA-00289: suggestion : D:\ORACLE\DATAFILES\ORA9I\ARCH\ORA9I_238.DBF&lt;br /&gt;ORA-00280: change 51119 for thread 1 is in sequence #238&lt;br /&gt;ORA-00278: log file 'D:\ORACLE\DATAFILES\ORA9I\ARCH\ORA9I_237.DBF' no&lt;br /&gt;longer needed for this recovery&lt;br /&gt;&lt;br /&gt;Specify log: {&lt;ret&gt;=suggested filename AUTO CANCEL}&lt;br /&gt;ORA-00279: change 51126 generated at 04/02/2008 22:49:51 needed for thread 1&lt;br /&gt;ORA-00289: suggestion : D:\ORACLE\DATAFILES\ORA9I\ARCH\ORA9I_239.DBF&lt;br /&gt;ORA-00280: change 51126 for thread 1 is in sequence #239&lt;br /&gt;ORA-00278: log file 'D:\ORACLE\DATAFILES\ORA9I\ARCH\ORA9I_238.DBF' no&lt;br /&gt;longer needed for this recovery&lt;br /&gt;&lt;br /&gt;Specify log: {&lt;ret&gt;=suggested filename AUTO CANCEL}&lt;br /&gt;ORA-00308: cannot open archived log 'D:\ORACLE\DATAFILES\ORA9I\ARCH\ORA9I_239.DBF'&lt;br /&gt;ORA-27041: unable to open file&lt;br /&gt;OSD-04002: unable to open file&lt;br /&gt;O/S-Error: (OS 2) The system cannot find the file specified.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The recovery is completed successfully. Here we have completed our switchover&lt;br /&gt;process.&lt;br /&gt;&lt;br /&gt;It is important to note that, in standby database, it is mandatory to have the database in force logging mode. This feature has been introduced by oracle in version 9i. For pre-9i versions, do check &lt;strong&gt;UNRECOVERABLE_TIME &lt;/strong&gt;column value of &lt;strong&gt;V$DATAFILE&lt;/strong&gt; view. This value must be less than the standby database creation time fir all existing datafiles. If any datafile is having value greater than standby creation time, we need to restore the backup of this datafile and continue with recovery. The unrecoverable_time value gets updated by oracle for any nologging operation. To avoid such nologging operations, do ensure that, &lt;strong&gt;all objects must be in logging mode.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Note – The above-mentioned steps can be used for switchback process.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-6978201667665325306?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/2Qddba4v3Js" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/6978201667665325306/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=6978201667665325306&amp;isPopup=true" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/6978201667665325306?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/6978201667665325306?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/2Qddba4v3Js/manual-switchover.html" title="Manual Switchover." /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2008/04/manual-switchover.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUQESHkzfip7ImA9WxZXFEk.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-361886420290743654</id><published>2008-03-02T12:11:00.010+05:30</published><updated>2008-03-02T13:05:09.786+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-02T13:05:09.786+05:30</app:edited><title>ASM File Handling.</title><content type="html">Automatic Storage Management (ASM) is a feature of Oracle Database 10g that provides integrated cluster file system and volume management capabilities at no additional cost. ASM provided a foundation for highly efficient storage management with kernelized asynchronous I/O, direct I/O, redundancy, striping, and an easy way to manage storage.&lt;br /&gt;&lt;br /&gt;ASM disk groups are not visible outside the database for regular file system administration tasks such as copying and creating directories. It is always seems to be difficult task to backup or migrating the database. But in reality it is not the case. Oracle has provided various methods to deal with ASM files, discussed in following sections –&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;ASM FILES HANDLING USING FTP&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;Starting with oracle 10g Release 2, Oracle has provided an option to transfer the files from ASM to locations outside of the diskgroups via FTP and ussing HTTP web browser.&lt;br /&gt;&lt;br /&gt;To access ASM files, file-mapping functionalities can be used provided by Oracle XML DB feature. Following is the method to setup FTP and HTTP to access and trasnfer the ASM files to regular OS files. FTP uses 21 as defualt port and HTTP uses port 80. ASM do not use default services, so to access ASM files, it is required to use two different unused ports. For e.g. we will use 8080 port for HTTP and 2100 port for FTP.&lt;br /&gt;&lt;br /&gt;1. Install XMLDB feature. &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt; &lt;pre&gt;   # cd $ORACLE_HOME/rdbms/admin&lt;br /&gt;   # sqlplus “/ as sysdba”&lt;br /&gt;&lt;br /&gt;   SQL&gt; @catqm.sql XDB SYSAUX TEMP&lt;br /&gt;&lt;br /&gt;   Here XDB is username&lt;br /&gt;        SYSAUX is default tablespace for xdb user.&lt;br /&gt;        TEMP is temporary tablespace for xdb user.&lt;br /&gt;&lt;br /&gt;   SQL&gt; @catxdbdbca 2100 8080&lt;br /&gt;&lt;br /&gt;   Here 2100 is FTP port.&lt;br /&gt;        8080 is HTTP port. &lt;/pre&gt;&lt;br /&gt;   Check for any invalid XDB objects and compile, if any.  &lt;br /&gt;&lt;br /&gt;   &lt;pre&gt;&lt;br /&gt;   SQL&gt; select object_name, object_type, status &lt;br /&gt;        from dba_objects&lt;br /&gt;        where owner=’XDB’ and status=’INVALID’;&lt;br /&gt;&lt;br /&gt;   SQL&gt; select comp_name, status, version from dba_registry&lt;br /&gt;        where comp_name=’Oracle XML Database’;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;2. Configure FTP and HTTP Ports.&lt;br /&gt;&lt;br /&gt;   &lt;blockquote&gt;&lt;pre&gt;   SQL&gt; execute dbms_xdb.sethttpport(8080);&lt;br /&gt;   SQL&gt; execute dbms_xdb.setftpport(2100);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;   You can also check the ports being used.&lt;br /&gt;&lt;br /&gt;   &lt;pre&gt;   Select dbms_xdb.gethttpport(), dbms_xdb.getftpport() &lt;br /&gt;   from dual;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;3. Listener and Instance modifications&lt;br /&gt;&lt;br /&gt;   &lt;blockquote&gt;For Single Instance – &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   SQL&gt; alter system set dispatchers = ‘(PROTOCOL=TCP) &lt;br /&gt;        (SERVICE=&lt;sid&gt;XDB)’ scope=both;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;   For RAC Instance – &lt;br /&gt;&lt;br /&gt;   &lt;pre&gt;   SQL&gt; alter system set dispatchers = ‘(PROTOCOL=TCP) &lt;br /&gt;        (SERVICE=&lt;sid1&gt;XDB)’ scope=both sid=INSTANCE1;&lt;br /&gt;&lt;br /&gt;   SQL&gt; alter system set dispatchers = ‘(PROTOCOL=TCP) &lt;br /&gt;        (SERVICE=&lt;sid2&gt;XDB)’ scope=both sid=INSTANCE2;&lt;/pre&gt;&lt;br /&gt; &lt;br /&gt;   Restart the listener&lt;br /&gt;&lt;br /&gt;   &lt;pre&gt;   # lsnrctl stop&lt;br /&gt;   # lsnrctl start&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;   Listener should show http and ftp port entries.&lt;br /&gt;&lt;br /&gt;   &lt;pre&gt;# lsnrctl status&lt;br /&gt;&lt;br /&gt;   ………… Truncated some entries ……………&lt;br /&gt;&lt;br /&gt;   Listening Endpoints Summary...&lt;br /&gt;&lt;br /&gt;   (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=test)(PORT=1521)))&lt;br /&gt;   (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=test)(PORT=1521)))&lt;br /&gt;   (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC)))&lt;br /&gt;   (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=test)(PORT=8080))(Presentation=HTTP)(Session=RAW))&lt;br /&gt;   (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=test)(PORT=2100))(Presentation=FTP)(Session=RAW))&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;4. Use FTP at O/S level to do interaction with ASM files.&lt;br /&gt;&lt;br /&gt;   &lt;blockquote&gt;&lt;pre&gt;   C:\&gt; ftp &lt;br /&gt;   ftp&gt; open test 2100     ---- Here test is the server name and 2100 is ftp port.&lt;br /&gt;        Username: system   ---- System is the database user.&lt;br /&gt;        Password: ******   ---- Password of database system user. &lt;br /&gt;&lt;br /&gt;   ftp&gt; cd /sys/asm&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;   Now directory can be changed to the directory containing the files to start FTP service and ls, get or put commands can be used as normal o/s level ftp command.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;DBMS_FILE_TRANSFER Utility&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Oracle also provides another feature to move files. The dbms_file_transfer utility introduced by oracle to copy/transfer the files to and from ASM. If you want to copy the file that is attached to a tablespace, you must put that tablespace into read-only mode or take it offline. Because of this limitation, you cannot copy the data files associated with the system, undo, sysaux or temporary tablespace. This utility treats the file as binary file during copy also character conversion is not possible during copy.&lt;br /&gt;&lt;br /&gt;Following is the method to transfer the files from file-system to ASM.&lt;br /&gt;&lt;br /&gt;List the file to copy.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL&gt; select file_name from dba_data_files &lt;br /&gt;  2  where tablespace_name=’USERS’;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Create directory for source and destination.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL&gt; create directory source_osfs as ‘/oracle/datafile’;&lt;br /&gt;SQL&gt; create directory dest_asmfs as ‘+asmdsk1/orcl/datafile';&lt;br /&gt;SQL&gt; grant read, write on source_osfs to testuser;&lt;br /&gt;SQL&gt; grant read, write on dest_asmfs to testuser;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Connect to the user and transfer the file.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL&gt; connect testuser/testuser@ora10gdb&lt;br /&gt;SQL&gt; alter tablespace users read only;&lt;br /&gt;     -OR-&lt;br /&gt;SQL&gt; alter tablespace users offline;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL&gt; exec dbms_transfer.copy_file (source_directory_object=&gt;’source_osfs’, source_file_name=&gt;’users01.dbf’, destination_directory_object=&gt;’dest_asmfs’, destination_file_name=&gt;’users01.dbf’);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;File has been copied so now datafile path can be changed using “alter database rename command”. Finally make the tablespace online for use.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL&gt; alter tablespace users read write;&lt;br /&gt;     -OR-&lt;br /&gt;SQL&gt; alter tablespace users online;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We can use get_file and put_file procedures of dbms_transfer package to copy the files across the database using database link.&lt;br /&gt;&lt;br /&gt;Limitations – &lt;br /&gt;&lt;br /&gt;1. File that we want to copy should be multiple of 512 bytes. Otherwise we would get following error. Also maximum file of file to transfer is 2 terabyte.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;ERROR at line 1: &lt;br /&gt;ORA-19505: failed to identify file "/oracle/datafile/users01.dbf" &lt;br /&gt;ORA-27046: file size is not a multiple of logical block size &lt;br /&gt;Additional information: 1 &lt;br /&gt;ORA-06512: at "SYS.DBMS_FILE_TRANSFER", line 84 &lt;br /&gt;ORA-06512: at "SYS.DBMS_FILE_TRANSFER", line 193 &lt;br /&gt;ORA-06512: at line 2 &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2. Till Oracle 10g Rel 1 only following transfer is allowed.&lt;br /&gt;&lt;br /&gt;ASM to ASM.&lt;br /&gt;OS file to OS file.&lt;br /&gt;&lt;br /&gt;This restriction is no more in Oracle 10g Rel 2. DBMS_TRANSFER has been enhanced to support following combinations of ASM.&lt;br /&gt;&lt;br /&gt;ASM to OS file.&lt;br /&gt;OS file to ASM.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;RMAN &amp; ASM FILES&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;So far we have completed two methods to copy the files from ASM to OS file system. Finally how can we forget RMAN utility to interact with ASM Files? Following is the method to copy the files using RMAN utility. We will copy the files from one disk group to another diskgroup. &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;1. Identify the file.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL&gt; select file_name from dba_data_files &lt;br /&gt;  2  where tablespace_name='TBS_TEST';&lt;br /&gt;&lt;br /&gt;FILE_NAME&lt;br /&gt;---------------------------------------------------&lt;br /&gt;+ORA_DG/oraprod/datafile/tbs_test.1442.642809147&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2. Take the file offline and copy it using rman utility.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL&gt; alter database datafile &lt;br /&gt;  2  ‘+ORA_DG/oraprod/datafile/tbs_test.1442.642809147’ offline;&lt;br /&gt;&lt;br /&gt;# rman target /&lt;br /&gt;&lt;br /&gt;RMAN&gt; COPY DATAFILE ‘+ORA_DG/oraprod/datafile/tbs_test.1442.642809147’ TO ‘+ORA_DG_NEW’;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;3. Now file has been copied, we can update the dictionary with the new ASM Diskgroup location.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL&gt; ALTER DATABASE RENAME FILE&lt;br /&gt;  2     ‘+ORA_DG/oraprod/datafile/tbs_test.1442.642809147’&lt;br /&gt;  3  TO ‘+ORA_DG_NEW/oraprod/datafile/tbs_test.264.59829765’;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We got the destination file name from rman copy output. &lt;br /&gt;&lt;br /&gt;4. Use RMAN to rename the ASM database file copy.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;RMAN&gt; switch datafile ‘+ORA_DG/oraprod/datafile/tbs_test. 264.59829765’ to copy;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;5. Finally recover and take the file online.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SQL&gt; Recover datafile ‘+ORA_DG/oraprod/datafile/tbs_test. 264.59829765’;&lt;br /&gt;&lt;br /&gt;SQL&gt; alter database datafile ‘+ORA_DG/oraprod/datafile/tbs_test. 264.59829765’ online;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;6. Delete the old file from ASM directory. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ export ORACLE_SID=+ASM&lt;br /&gt;$ sqlplus “/ as sysdba” &lt;br /&gt;SQL&gt; alter diskgroup ora_dg_new drop file datafile ‘+ORA_DG/oraprod/datafile/tbs_test. 264.59829765’;&lt;/pre&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-361886420290743654?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/9nIiOM3Zubk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/361886420290743654/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=361886420290743654&amp;isPopup=true" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/361886420290743654?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/361886420290743654?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/9nIiOM3Zubk/asm-file-handling.html" title="ASM File Handling." /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2008/03/asm-file-handling.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkIHQH4yfip7ImA9WB9bGU4.&quot;"><id>tag:blogger.com,1999:blog-6890489880673875730.post-9020002880785873293</id><published>2007-12-29T17:08:00.000+05:30</published><updated>2007-12-29T17:45:31.096+05:30</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-29T17:45:31.096+05:30</app:edited><title>Index Internals.</title><content type="html">Tables can grow large, and when they do, it becomes difficult for users to quickly find the data they need. Oracle offers indexes as a method of speeding database performance when accessing tables with a lot of data. The b-tree index is the traditional and most useful indexing mechanism. It stores the data in a tree like structure.&lt;br /&gt;&lt;br /&gt;The b-tree index structure is made up of root block, branch blocks and leaf blocks. The root block is an entry point where search for data in index starts. Any index contains only and only one root block. The root block is always physically the same block. The branch blocks are next level of the root block that is having pointers to leaf blocks in the index.&lt;br /&gt;&lt;br /&gt;The leaf blocks are the highest level of the index, which contain indexed column values and the corresponding ROWIDs. Each leaf block is comprised of double-linked list structure. It means each leaf block is linked to the other block on its left and right, in order to make it possible to search in both the directions through a range of values in index. The index entries are always in ordered. &lt;br /&gt;&lt;br /&gt;The oracle's index is always maintains the balanced structure. To understand this, it is necessary to understand block split operation in index. There are two ways of block splits&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;50-50 BLOCK SPLIT &lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;The 50-50-block split can occur when there is an insert operation of a non-maximum value and when the corresponding block is full. The indexed column update operation for an index is internally delete followed by an insert. The split operation steps are as follows –&lt;br /&gt;&lt;br /&gt;1. Request for new block from free-list/bitmap structure (Depending on non-ASSM and ASSM tablespace option)&lt;br /&gt;2. Distribute existing block so that upper half volume of an index move to the new requested block&lt;br /&gt;3. Insert the column value in appropriate block.&lt;br /&gt;4. Update the leaf block pointers such that previously full block right pointer will point to the new block and new block’s right pointer will point to the right pointed block of previously full block.&lt;br /&gt;5. Finally update the branch block to reference previous full block and add a new entry for to point to new leaf block.&lt;br /&gt;&lt;br /&gt;The similar kind of operation is applicable to the branch and root block split. Even branch and root block split is more expensive as it involves corresponding next level pointer updations. Root block split allocate two new blocks wherein data is evenly distributed and root block is updated such that it will now point to these new blocks. So root block will always physically the same block. The root block split can increase the height of the index by 1. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;90-10 BLOCK SPLIT&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;The 90-10 block split can occur, when the new indexed column entry is the maximum value. In this case, new block will be requested and corresponding branch blocks are updated accordingly.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Can deleted space of an index be reused?&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;There are multiple answers to this question – &lt;br /&gt;  1. Index will never use deleted space.&lt;br /&gt;  2. Index will use deleted space if the same column value is inserted again.&lt;br /&gt;&lt;br /&gt;But in reality, both the above statements are myth. Index will use deleted space even when the new inserted value is not same.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Test Case – &lt;/pre&gt;&lt;br /&gt;To validate above statemenet, we will create the test table and insert some records into it. Here temp table contains the serial values.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create table temp as select rownum "A" from dba_objects a, dba_objects;&lt;br /&gt;&lt;br /&gt;Table created.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; create table test (a number, b number);&lt;br /&gt;&lt;br /&gt;Table created.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; create index ind_test on test(a);&lt;br /&gt;&lt;br /&gt;Index created.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; insert into test select a, a+50000 from temp where a &gt;10000 and a&lt;=20000;&lt;br /&gt;&lt;br /&gt;10000 rows created.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; analyze index ind_test validate structure;&lt;br /&gt;&lt;br /&gt;Index analyzed.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select name, lf_rows, del_lf_rows, used_space &lt;br /&gt;     From index_stats where name='IND_TEST';&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;NAME                              LF_ROWS DEL_LF_ROWS USED_SPACE&lt;br /&gt;------------------------------ ---------- ----------- ----------&lt;br /&gt;IND_TEST                            10000           0     160127&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As LF_ROWS column shows, there are 10000 records present in the index. Now check the values after deletion of some records.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; delete from test where a &gt; 14000 and a &lt;= 16000;&lt;br /&gt;&lt;br /&gt;2000 rows deleted.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; analyze index ind_test validate structure;&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;Index analyzed.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select name, lf_rows, del_lf_rows, used_space from index_stats   &lt;br /&gt;     where name='IND_TEST';&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;NAME          LF_ROWS DEL_LF_ROWS USED_SPACE&lt;br /&gt;---------- ---------- ----------- ----------&lt;br /&gt;IND_TEST        10000        2000     160127&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select count(*) from test;&lt;br /&gt;&lt;br /&gt;  COUNT(*)&lt;br /&gt;----------&lt;br /&gt;      8000&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here the number of rows (LF_ROWS – DEL_LF_ROWS) = (10000-2000) = 8000 which matches the count of the test table. &lt;br /&gt;&lt;br /&gt;To check whether index uses the deleted space, we will add new records which are not same as the current or deleted rows.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; insert into test select a, a+50000 from temp where a &gt; 20000 and a&lt;=22000;&lt;br /&gt;&lt;br /&gt;2000 rows created.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; analyze index ind_test validate structure;&lt;br /&gt;&lt;br /&gt;Index analyzed.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select name, lf_rows, del_lf_rows, used_space from index_stats &lt;br /&gt;     where name='IND_TEST';&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;NAME          LF_ROWS DEL_LF_ROWS USED_SPACE&lt;br /&gt;---------- ---------- ----------- ----------&lt;br /&gt;IND_TEST        10500         500     168146&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; select count(1) from test;&lt;br /&gt;&lt;br /&gt;  COUNT(1)&lt;br /&gt;----------&lt;br /&gt;     10000&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The total added rows are 2000. As the del_lf_rows column shows, there are only 500 deleted records found. Which means, out of 2000 deleted records, the index has used deleted space of 1500 records. Now check the status after insertion of the some previously deleted records. We had deleted records between 14000 and 16000 values, and now will insert any of these 500 values.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL&gt; insert into test select a, a+1000 &lt;br /&gt;     From temp where a &gt; 14500 and a &lt;= 15500 and mod(a,2)=0;&lt;br /&gt;&lt;br /&gt;500 rows created.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; analyze index ind_test validate structure;&lt;br /&gt;&lt;br /&gt;Index analyzed.&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select name, lf_rows, del_lf_rows, used_space &lt;br /&gt;     From index_stats where name='IND_TEST';&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;NAME          LF_ROWS DEL_LF_ROWS USED_SPACE&lt;br /&gt;---------- ---------- ----------- ----------&lt;br /&gt;IND_TEST        10500           0     168141&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The above statistics shows that index will always use the deleted space.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Conclusion – &lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;1. Rows deletions will either make the blocks empty or some space in block. &lt;br /&gt;2. The empty blocks can be used for any rows irrespective of previous deleted column values. &lt;br /&gt;3. If there is space available in existing blocks, space can be used if it satifies the column value between lower and upper existing values in that block.&lt;br /&gt;&lt;br /&gt;In many cases, it has also been observed that DBA's rebuild indexes to reclaim deleted space, but the test case above shows that the deleted space is reclaimed and hence does not require any rebuilding. Hence, if this is one of the primary reason to rebuild the indexes, then, dba's now can wonder whether do they really require index rebuilding?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6890489880673875730-9020002880785873293?l=sandeepredkar.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/SandeepRedkar/~4/7XvGJG9fDBk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://sandeepredkar.blogspot.com/feeds/9020002880785873293/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=6890489880673875730&amp;postID=9020002880785873293&amp;isPopup=true" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/9020002880785873293?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/6890489880673875730/posts/default/9020002880785873293?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/SandeepRedkar/~3/7XvGJG9fDBk/index-internals.html" title="Index Internals." /><author><name>Sandeep Redkar</name><uri>http://www.blogger.com/profile/09850590013696861549</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>4</thr:total><feedburner:origLink>http://sandeepredkar.blogspot.com/2007/12/index-internals.html</feedburner:origLink></entry></feed>

