<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
		>
<channel>
	<title>Comments on: Interesting Index &#8220;Facts&#8221; &#8211; What is Wrong with these Quotes?</title>
	<atom:link href="http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/feed/" rel="self" type="application/rss+xml" />
	<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/</link>
	<description>Miscellaneous Random Oracle Topics: Stop, Think, ... Understand</description>
	<lastBuildDate>Thu, 23 May 2013 04:02:42 +0000</lastBuildDate>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
	<item>
		<title>By: David Fitzjarrell (@ddfdba)</title>
		<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/#comment-4487</link>
		<dc:creator><![CDATA[David Fitzjarrell (@ddfdba)]]></dc:creator>
		<pubDate>Wed, 29 Feb 2012 18:44:09 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6083#comment-4487</guid>
		<description><![CDATA[Coming in on the tail end of this:

&lt;pre&gt;
SQL&gt; 
SQL&gt; --
SQL&gt; -- I  creates a index indx1 on (C1,C2,C3). I issue three select
SQL&gt; -- statements like :
SQL&gt; --
SQL&gt; -- 1.select * from T1 where C1=
SQL&gt; -- 2.select * from T1 where C2=
SQL&gt; -- 3.select * from T1 where C3=
SQL&gt; -- 4.select * from T1 where C1=  and  C2=  and C3 =
SQL&gt; --
SQL&gt; --
SQL&gt; --
SQL&gt; --
SQL&gt; -- Regards,
SQL&gt; -- Sanjoy
SQL&gt; --
SQL&gt; 
SQL&gt; --
SQL&gt; -- Create the table in question
SQL&gt; --
SQL&gt; create table t1(
  2  	   c1 number,
  3  	   c2 varchar2(20),
  4  	   c3 date,
  5  	   c4 varchar2(10),
  6  	   c5 number,
  7  	   c6 number
  8  );

Table created.

SQL&gt; 
SQL&gt; 
SQL&gt; --
SQL&gt; -- Create the index specified
SQL&gt; --
SQL&gt; create index indx1
  2  on t1(c1,c2,c3);

Index created.

SQL&gt; 
SQL&gt; 
SQL&gt; --
SQL&gt; -- Load test data
SQL&gt; --
SQL&gt; 
SQL&gt; --
SQL&gt; -- Data with unique C1, C2 and C3 values
SQL&gt; --
SQL&gt; begin
  2  	   for i in 1..10101 loop
  3  	    insert into t1
  4  	    values (i, &#039;Testing record &#039;&#124;&#124;i, trunc(sysdate+i), &#039;Filler&#039;, mod(i, 734), mod(i, 963));
  5  	   end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

SQL&gt; 
SQL&gt; 
SQL&gt; commit;

Commit complete.

SQL&gt; 
SQL&gt; 
SQL&gt; --
SQL&gt; -- &#039;Standard&#039; statistics
SQL&gt; --
SQL&gt; exec dbms_stats.gather_schema_stats(ownname=&gt;&#039;BING&#039;);

PL/SQL procedure successfully completed.

SQL&gt; 
SQL&gt; 
SQL&gt; 
SQL&gt; set autotrace on linesize 132
SQL&gt; 
SQL&gt; select * From t1 where c1 =433;

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
       433 Testing record 433   07-MAY-13 Filler            433        433


Execution Plan
----------------------------------------------------------
Plan hash value: 552572096

-------------------------------------------------------------------------------------
&#124; Id  &#124; Operation                   &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost (%CPU)&#124; Time     &#124;
-------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT            &#124;       &#124;     1 &#124;    47 &#124;     1   (0)&#124; 00:00:01 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;     1 &#124;    47 &#124;     1   (0)&#124; 00:00:01 &#124;
&#124;*  2 &#124;   INDEX RANGE SCAN          &#124; INDX1 &#124;     1 &#124;       &#124;     1   (0)&#124; 00:00:01 &#124;
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(&quot;C1&quot;=433)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
        739  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * From t1 where c2 = &#039;Testing record 7748&#039;;

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
      7748 Testing record 7748  17-MAY-33 Filler            408         44


Execution Plan
----------------------------------------------------------
Plan hash value: 3325179317

-------------------------------------------------------------------------------------
&#124; Id  &#124; Operation                   &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost (%CPU)&#124; Time     &#124;
-------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT            &#124;       &#124;     1 &#124;    47 &#124;    11   (0)&#124; 00:00:01 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;     1 &#124;    47 &#124;    11   (0)&#124; 00:00:01 &#124;
&#124;*  2 &#124;   INDEX SKIP SCAN           &#124; INDX1 &#124;     1 &#124;       &#124;    11   (0)&#124; 00:00:01 &#124;
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(&quot;C2&quot;=&#039;Testing record 7748&#039;)
       filter(&quot;C2&quot;=&#039;Testing record 7748&#039;)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         59  consistent gets
          0  physical reads
          0  redo size
        739  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * from t1 where c3 = trunc(sysdate+9);

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
         9 Testing record 9     09-MAR-12 Filler              9          9


Execution Plan
----------------------------------------------------------
Plan hash value: 3325179317

-------------------------------------------------------------------------------------
&#124; Id  &#124; Operation                   &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost (%CPU)&#124; Time     &#124;
-------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT            &#124;       &#124;     1 &#124;    47 &#124;    11   (0)&#124; 00:00:01 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;     1 &#124;    47 &#124;    11   (0)&#124; 00:00:01 &#124;
&#124;*  2 &#124;   INDEX SKIP SCAN           &#124; INDX1 &#124;     1 &#124;       &#124;    11   (0)&#124; 00:00:01 &#124;
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(&quot;C3&quot;=TRUNC(SYSDATE@!+9))
       filter(&quot;C3&quot;=TRUNC(SYSDATE@!+9))


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         59  consistent gets
          0  physical reads
          0  redo size
        734  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * From t1 where c1 = 9993 and c2 = &#039;Testing record 9993&#039; and c3 = trunc(sysdate+9993);

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
      9993 Testing record 9993  10-JUL-39 Filler            451        363


Execution Plan
----------------------------------------------------------
Plan hash value: 552572096

-------------------------------------------------------------------------------------
&#124; Id  &#124; Operation                   &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost (%CPU)&#124; Time     &#124;
-------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT            &#124;       &#124;     1 &#124;    47 &#124;     1   (0)&#124; 00:00:01 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;     1 &#124;    47 &#124;     1   (0)&#124; 00:00:01 &#124;
&#124;*  2 &#124;   INDEX RANGE SCAN          &#124; INDX1 &#124;     1 &#124;       &#124;     1   (0)&#124; 00:00:01 &#124;
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(&quot;C1&quot;=9993 AND &quot;C2&quot;=&#039;Testing record 9993&#039; AND
              &quot;C3&quot;=TRUNC(SYSDATE@!+9993))


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
        740  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; set autotrace off
SQL&gt; 
SQL&gt; --
SQL&gt; -- Statistics with auto-sized histograms on indexed columns
SQL&gt; --
SQL&gt; exec dbms_stats.gather_schema_stats(ownname=&gt;&#039;BING&#039;, method_opt =&gt; &#039;FOR ALL INDEXED COLUMNS SIZE AUTO&#039;);

PL/SQL procedure successfully completed.

SQL&gt; 
SQL&gt; set autotrace on linesize 132
SQL&gt; 
SQL&gt; select * From t1 where c1 =433;

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
       433 Testing record 433   07-MAY-13 Filler            433        433


Execution Plan
----------------------------------------------------------
Plan hash value: 552572096

-------------------------------------------------------------------------------------
&#124; Id  &#124; Operation                   &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost (%CPU)&#124; Time     &#124;
-------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT            &#124;       &#124;     1 &#124;    47 &#124;     1   (0)&#124; 00:00:01 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;     1 &#124;    47 &#124;     1   (0)&#124; 00:00:01 &#124;
&#124;*  2 &#124;   INDEX RANGE SCAN          &#124; INDX1 &#124;     1 &#124;       &#124;     1   (0)&#124; 00:00:01 &#124;
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(&quot;C1&quot;=433)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
        739  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * From t1 where c2 = &#039;Testing record 7748&#039;;

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
      7748 Testing record 7748  17-MAY-33 Filler            408         44


Execution Plan
----------------------------------------------------------
Plan hash value: 3325179317

-------------------------------------------------------------------------------------
&#124; Id  &#124; Operation                   &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost (%CPU)&#124; Time     &#124;
-------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT            &#124;       &#124;     1 &#124;    47 &#124;    11   (0)&#124; 00:00:01 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;     1 &#124;    47 &#124;    11   (0)&#124; 00:00:01 &#124;
&#124;*  2 &#124;   INDEX SKIP SCAN           &#124; INDX1 &#124;     1 &#124;       &#124;    11   (0)&#124; 00:00:01 &#124;
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(&quot;C2&quot;=&#039;Testing record 7748&#039;)
       filter(&quot;C2&quot;=&#039;Testing record 7748&#039;)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         59  consistent gets
          0  physical reads
          0  redo size
        739  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * from t1 where c3 = trunc(sysdate+9);

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
         9 Testing record 9     09-MAR-12 Filler              9          9


Execution Plan
----------------------------------------------------------
Plan hash value: 3325179317

-------------------------------------------------------------------------------------
&#124; Id  &#124; Operation                   &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost (%CPU)&#124; Time     &#124;
-------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT            &#124;       &#124;     1 &#124;    47 &#124;    11   (0)&#124; 00:00:01 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;     1 &#124;    47 &#124;    11   (0)&#124; 00:00:01 &#124;
&#124;*  2 &#124;   INDEX SKIP SCAN           &#124; INDX1 &#124;     1 &#124;       &#124;    11   (0)&#124; 00:00:01 &#124;
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(&quot;C3&quot;=TRUNC(SYSDATE@!+9))
       filter(&quot;C3&quot;=TRUNC(SYSDATE@!+9))


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         59  consistent gets
          0  physical reads
          0  redo size
        734  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * From t1 where c1 = 9993 and c2 = &#039;Testing record 9993&#039; and c3 = trunc(sysdate+9993);

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
      9993 Testing record 9993  10-JUL-39 Filler            451        363


Execution Plan
----------------------------------------------------------
Plan hash value: 552572096

-------------------------------------------------------------------------------------
&#124; Id  &#124; Operation                   &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost (%CPU)&#124; Time     &#124;
-------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT            &#124;       &#124;     1 &#124;    47 &#124;     1   (0)&#124; 00:00:01 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID&#124; T1    &#124;     1 &#124;    47 &#124;     1   (0)&#124; 00:00:01 &#124;
&#124;*  2 &#124;   INDEX RANGE SCAN          &#124; INDX1 &#124;     1 &#124;       &#124;     1   (0)&#124; 00:00:01 &#124;
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(&quot;C1&quot;=9993 AND &quot;C2&quot;=&#039;Testing record 9993&#039; AND
              &quot;C3&quot;=TRUNC(SYSDATE@!+9993))


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
        740  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
&lt;/pre&gt;

The behaviour changed from 10gR2 (shown here: http://oratips-ddf.blogspot.com/2008/11/magical-indexes.html) as any query not  using the leading column in 11gR2 uses in index skip scan for concatenated indexes should other indexed columns be specified in the where clause (as shown above).


I haven&#039;t the time to pursue the other comments at this point; I agree with Charles&#039; comments and had the same thoujghts as I read through the list of quotes.]]></description>
		<content:encoded><![CDATA[<p>Coming in on the tail end of this:</p>
<pre>
SQL&gt; 
SQL&gt; --
SQL&gt; -- I  creates a index indx1 on (C1,C2,C3). I issue three select
SQL&gt; -- statements like :
SQL&gt; --
SQL&gt; -- 1.select * from T1 where C1=
SQL&gt; -- 2.select * from T1 where C2=
SQL&gt; -- 3.select * from T1 where C3=
SQL&gt; -- 4.select * from T1 where C1=  and  C2=  and C3 =
SQL&gt; --
SQL&gt; --
SQL&gt; --
SQL&gt; --
SQL&gt; -- Regards,
SQL&gt; -- Sanjoy
SQL&gt; --
SQL&gt; 
SQL&gt; --
SQL&gt; -- Create the table in question
SQL&gt; --
SQL&gt; create table t1(
  2  	   c1 number,
  3  	   c2 varchar2(20),
  4  	   c3 date,
  5  	   c4 varchar2(10),
  6  	   c5 number,
  7  	   c6 number
  8  );

Table created.

SQL&gt; 
SQL&gt; 
SQL&gt; --
SQL&gt; -- Create the index specified
SQL&gt; --
SQL&gt; create index indx1
  2  on t1(c1,c2,c3);

Index created.

SQL&gt; 
SQL&gt; 
SQL&gt; --
SQL&gt; -- Load test data
SQL&gt; --
SQL&gt; 
SQL&gt; --
SQL&gt; -- Data with unique C1, C2 and C3 values
SQL&gt; --
SQL&gt; begin
  2  	   for i in 1..10101 loop
  3  	    insert into t1
  4  	    values (i, 'Testing record '||i, trunc(sysdate+i), 'Filler', mod(i, 734), mod(i, 963));
  5  	   end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

SQL&gt; 
SQL&gt; 
SQL&gt; commit;

Commit complete.

SQL&gt; 
SQL&gt; 
SQL&gt; --
SQL&gt; -- 'Standard' statistics
SQL&gt; --
SQL&gt; exec dbms_stats.gather_schema_stats(ownname=&gt;'BING');

PL/SQL procedure successfully completed.

SQL&gt; 
SQL&gt; 
SQL&gt; 
SQL&gt; set autotrace on linesize 132
SQL&gt; 
SQL&gt; select * From t1 where c1 =433;

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
       433 Testing record 433   07-MAY-13 Filler            433        433


Execution Plan
----------------------------------------------------------
Plan hash value: 552572096

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

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

   2 - access("C1"=433)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
        739  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * From t1 where c2 = 'Testing record 7748';

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
      7748 Testing record 7748  17-MAY-33 Filler            408         44


Execution Plan
----------------------------------------------------------
Plan hash value: 3325179317

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     1 |    47 |    11   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |     1 |    47 |    11   (0)| 00:00:01 |
|*  2 |   INDEX SKIP SCAN           | INDX1 |     1 |       |    11   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("C2"='Testing record 7748')
       filter("C2"='Testing record 7748')


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         59  consistent gets
          0  physical reads
          0  redo size
        739  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * from t1 where c3 = trunc(sysdate+9);

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
         9 Testing record 9     09-MAR-12 Filler              9          9


Execution Plan
----------------------------------------------------------
Plan hash value: 3325179317

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     1 |    47 |    11   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |     1 |    47 |    11   (0)| 00:00:01 |
|*  2 |   INDEX SKIP SCAN           | INDX1 |     1 |       |    11   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("C3"=TRUNC(SYSDATE@!+9))
       filter("C3"=TRUNC(SYSDATE@!+9))


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         59  consistent gets
          0  physical reads
          0  redo size
        734  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * From t1 where c1 = 9993 and c2 = 'Testing record 9993' and c3 = trunc(sysdate+9993);

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
      9993 Testing record 9993  10-JUL-39 Filler            451        363


Execution Plan
----------------------------------------------------------
Plan hash value: 552572096

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

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

   2 - access("C1"=9993 AND "C2"='Testing record 9993' AND
              "C3"=TRUNC(SYSDATE@!+9993))


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
        740  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; set autotrace off
SQL&gt; 
SQL&gt; --
SQL&gt; -- Statistics with auto-sized histograms on indexed columns
SQL&gt; --
SQL&gt; exec dbms_stats.gather_schema_stats(ownname=&gt;'BING', method_opt =&gt; 'FOR ALL INDEXED COLUMNS SIZE AUTO');

PL/SQL procedure successfully completed.

SQL&gt; 
SQL&gt; set autotrace on linesize 132
SQL&gt; 
SQL&gt; select * From t1 where c1 =433;

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
       433 Testing record 433   07-MAY-13 Filler            433        433


Execution Plan
----------------------------------------------------------
Plan hash value: 552572096

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

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

   2 - access("C1"=433)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
        739  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * From t1 where c2 = 'Testing record 7748';

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
      7748 Testing record 7748  17-MAY-33 Filler            408         44


Execution Plan
----------------------------------------------------------
Plan hash value: 3325179317

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     1 |    47 |    11   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |     1 |    47 |    11   (0)| 00:00:01 |
|*  2 |   INDEX SKIP SCAN           | INDX1 |     1 |       |    11   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("C2"='Testing record 7748')
       filter("C2"='Testing record 7748')


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         59  consistent gets
          0  physical reads
          0  redo size
        739  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * from t1 where c3 = trunc(sysdate+9);

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
         9 Testing record 9     09-MAR-12 Filler              9          9


Execution Plan
----------------------------------------------------------
Plan hash value: 3325179317

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     1 |    47 |    11   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |     1 |    47 |    11   (0)| 00:00:01 |
|*  2 |   INDEX SKIP SCAN           | INDX1 |     1 |       |    11   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("C3"=TRUNC(SYSDATE@!+9))
       filter("C3"=TRUNC(SYSDATE@!+9))


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         59  consistent gets
          0  physical reads
          0  redo size
        734  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
SQL&gt; select * From t1 where c1 = 9993 and c2 = 'Testing record 9993' and c3 = trunc(sysdate+9993);

        C1 C2                   C3        C4                 C5         C6
---------- -------------------- --------- ---------- ---------- ----------
      9993 Testing record 9993  10-JUL-39 Filler            451        363


Execution Plan
----------------------------------------------------------
Plan hash value: 552572096

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

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

   2 - access("C1"=9993 AND "C2"='Testing record 9993' AND
              "C3"=TRUNC(SYSDATE@!+9993))


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
        740  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL&gt; 
</pre>
<p>The behaviour changed from 10gR2 (shown here: <a href="http://oratips-ddf.blogspot.com/2008/11/magical-indexes.html" rel="nofollow">http://oratips-ddf.blogspot.com/2008/11/magical-indexes.html</a>) as any query not  using the leading column in 11gR2 uses in index skip scan for concatenated indexes should other indexed columns be specified in the where clause (as shown above).</p>
<p>I haven&#8217;t the time to pursue the other comments at this point; I agree with Charles&#8217; comments and had the same thoujghts as I read through the list of quotes.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/#comment-4460</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Sun, 26 Feb 2012 17:49:41 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6083#comment-4460</guid>
		<description><![CDATA[Mich,

Jimmyb&#039;s test case above shows an &lt;b&gt;INDEX SKIP  SCAN&lt;/b&gt; operation that had as a parent operation &lt;b&gt;TABLE ACCESS BY INDEX ROWID&lt;/b&gt;

I do not like the phrase &quot;cardinality of leading column&quot; - think that I recently saw a similar phase in the Oracle documentation.  To some people (for better or worse, that includes me), that phrase may mean &quot;If I select all of the rows from the table with a particular value specified for the leading column of the index, the optimizer&#039;s predicted number of rows (cardinality) will only be a few rows&quot; - that would imply that there are many different values in the leading column of the index.  To other people, that phrase may mean that there are only a couple of unique values in the leading column of the index.  I think that it is a bit more clear to state that index skip scans are potentially useful if the number of distinct values in the leading column(s) is low.

The optimizer&#039;s costing of skip scans provides a clue how skip scans are processed.  If you just execute the first half of jimmyb&#039;s test case script, you will have a table T with 10,000 distinct values in the leading column of the T_IDX index.  If you attempt to force an index skip scan operation in this case, you will probably find that the optimizer calculated a cost for the INDEX SKIP SCAN of approximately 10,005 - which suggests costing of 10,000 sub-index accesses (I have not worked out exactly why the cost is 5 more than 10,000, but the access to the index root block and branch block(s) need to be considered and also the CPU component of the cost):
&lt;pre&gt;
select /*+ index_ss(t) */ * from t where code = 1;
 
select * from table(dbms_xplan.display_cursor(null,null,&#039;allstats last +cost&#039;));
 
SQL_ID  22ru4c22nssxb, child number 0
-------------------------------------
select /*+ index_ss(t) */ * from t where code = 1
 
Plan hash value: 2053318169
 
------------------------------------------------------------------------------------------------------------
&#124; Id  &#124; Operation                   &#124; Name  &#124; Starts &#124; E-Rows &#124; Cost (%CPU)&#124; A-Rows &#124;   A-Time   &#124; Buffers &#124;
------------------------------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT            &#124;       &#124;      1 &#124;        &#124; 10006 (100)&#124;    100 &#124;00:00:00.01 &#124;      97 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID&#124; T     &#124;      1 &#124;    100 &#124; 10006   (1)&#124;    100 &#124;00:00:00.01 &#124;      97 &#124;
&#124;*  2 &#124;   INDEX SKIP SCAN           &#124; T_IDX &#124;      1 &#124;    100 &#124; 10005   (1)&#124;    100 &#124;00:00:00.01 &#124;      41 &#124;
------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(&quot;CODE&quot;=1)
       filter(&quot;CODE&quot;=1)
&lt;/pre&gt;

From the documentation:
http://docs.oracle.com/cd/E11882_01/server.112/e16638/optimops.htm#autoId22
&lt;blockquote&gt;
Index skip scans improve index scans by nonprefix columns. Often, scanning index blocks is faster than scanning table data blocks.

Skip scanning lets a composite index be split logically into smaller subindexes. In skip scanning, the initial column of the composite index is not specified in the query. In other words, it is skipped.

The database determines the number of logical subindexes by the number of distinct values in the initial column. Skip scanning is advantageous when there are few distinct values in the leading column of the composite index and many distinct values in the nonleading key of the index.

The database may choose an index skip scan when the leading column of the composite index is not specified in a query predicate.
&lt;/blockquote&gt;]]></description>
		<content:encoded><![CDATA[<p>Mich,</p>
<p>Jimmyb&#8217;s test case above shows an <b>INDEX SKIP  SCAN</b> operation that had as a parent operation <b>TABLE ACCESS BY INDEX ROWID</b></p>
<p>I do not like the phrase &#8220;cardinality of leading column&#8221; &#8211; think that I recently saw a similar phase in the Oracle documentation.  To some people (for better or worse, that includes me), that phrase may mean &#8220;If I select all of the rows from the table with a particular value specified for the leading column of the index, the optimizer&#8217;s predicted number of rows (cardinality) will only be a few rows&#8221; &#8211; that would imply that there are many different values in the leading column of the index.  To other people, that phrase may mean that there are only a couple of unique values in the leading column of the index.  I think that it is a bit more clear to state that index skip scans are potentially useful if the number of distinct values in the leading column(s) is low.</p>
<p>The optimizer&#8217;s costing of skip scans provides a clue how skip scans are processed.  If you just execute the first half of jimmyb&#8217;s test case script, you will have a table T with 10,000 distinct values in the leading column of the T_IDX index.  If you attempt to force an index skip scan operation in this case, you will probably find that the optimizer calculated a cost for the INDEX SKIP SCAN of approximately 10,005 &#8211; which suggests costing of 10,000 sub-index accesses (I have not worked out exactly why the cost is 5 more than 10,000, but the access to the index root block and branch block(s) need to be considered and also the CPU component of the cost):</p>
<pre>
select /*+ index_ss(t) */ * from t where code = 1;
 
select * from table(dbms_xplan.display_cursor(null,null,'allstats last +cost'));
 
SQL_ID  22ru4c22nssxb, child number 0
-------------------------------------
select /*+ index_ss(t) */ * from t where code = 1
 
Plan hash value: 2053318169
 
------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |      1 |        | 10006 (100)|    100 |00:00:00.01 |      97 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |      1 |    100 | 10006   (1)|    100 |00:00:00.01 |      97 |
|*  2 |   INDEX SKIP SCAN           | T_IDX |      1 |    100 | 10005   (1)|    100 |00:00:00.01 |      41 |
------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("CODE"=1)
       filter("CODE"=1)
</pre>
<p>From the documentation:<br />
<a href="http://docs.oracle.com/cd/E11882_01/server.112/e16638/optimops.htm#autoId22" rel="nofollow">http://docs.oracle.com/cd/E11882_01/server.112/e16638/optimops.htm#autoId22</a></p>
<blockquote><p>
Index skip scans improve index scans by nonprefix columns. Often, scanning index blocks is faster than scanning table data blocks.</p>
<p>Skip scanning lets a composite index be split logically into smaller subindexes. In skip scanning, the initial column of the composite index is not specified in the query. In other words, it is skipped.</p>
<p>The database determines the number of logical subindexes by the number of distinct values in the initial column. Skip scanning is advantageous when there are few distinct values in the leading column of the composite index and many distinct values in the nonleading key of the index.</p>
<p>The database may choose an index skip scan when the leading column of the composite index is not specified in a query predicate.
</p></blockquote>
]]></content:encoded>
	</item>
	<item>
		<title>By: Mich talebzadeh</title>
		<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/#comment-4459</link>
		<dc:creator><![CDATA[Mich talebzadeh]]></dc:creator>
		<pubDate>Sun, 26 Feb 2012 17:03:49 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6083#comment-4459</guid>
		<description><![CDATA[Hi Charles,

Your point

&quot;#3: The book states, “… this test allows us to dispel a myth. Oracle uses the indexes even if the leading columns are not referenced in the WHERE predicate of the query. We can see that in such a case, the operation will be an INDEX FAST FULL SCAN.” In this case, the author is incorrectly attempting to generalize a special case into a general rule. Firstly, there is no myth to dispel – Oracle’s query optimizer has had the ability to use INDEX SKIP SCAN operations when the leading column of an index is not specified in the WHERE clause, since the release of Oracle Database 9.0.1 a decade ago – but that access path is usually only advisable when there are few distinct values in the leading column of the index. The author’s test case is a special case because all of the columns selected from the table are present in the index structure. &quot;

Please someone correct me if I am wrong as if there seems to be a bit of confusion about &quot;index skip scan&quot;. Am I correct to state that &quot;index skip scan&quot; is only applicable to cases where the index itself covers the query and the base table need not be touched? In that case I am not sure there is other case where index skip scan can be deployed. If the index cannot cover the query alone then there will not be much gained from &quot;index skip scan&quot;?

Some examples of index skip scan in web are if I use the phrase are naive so to speak. For example many think that Oracle will choose s index skip scan  if the leading column is binary type (say male or female). That is not the case. The case is vaild as long as the cardinality of leading column is pretty small.

Cheers,

Mich]]></description>
		<content:encoded><![CDATA[<p>Hi Charles,</p>
<p>Your point</p>
<p>&#8220;#3: The book states, “… this test allows us to dispel a myth. Oracle uses the indexes even if the leading columns are not referenced in the WHERE predicate of the query. We can see that in such a case, the operation will be an INDEX FAST FULL SCAN.” In this case, the author is incorrectly attempting to generalize a special case into a general rule. Firstly, there is no myth to dispel – Oracle’s query optimizer has had the ability to use INDEX SKIP SCAN operations when the leading column of an index is not specified in the WHERE clause, since the release of Oracle Database 9.0.1 a decade ago – but that access path is usually only advisable when there are few distinct values in the leading column of the index. The author’s test case is a special case because all of the columns selected from the table are present in the index structure. &#8221;</p>
<p>Please someone correct me if I am wrong as if there seems to be a bit of confusion about &#8220;index skip scan&#8221;. Am I correct to state that &#8220;index skip scan&#8221; is only applicable to cases where the index itself covers the query and the base table need not be touched? In that case I am not sure there is other case where index skip scan can be deployed. If the index cannot cover the query alone then there will not be much gained from &#8220;index skip scan&#8221;?</p>
<p>Some examples of index skip scan in web are if I use the phrase are naive so to speak. For example many think that Oracle will choose s index skip scan  if the leading column is binary type (say male or female). That is not the case. The case is vaild as long as the cardinality of leading column is pretty small.</p>
<p>Cheers,</p>
<p>Mich</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/#comment-4457</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Sun, 26 Feb 2012 00:17:13 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6083#comment-4457</guid>
		<description><![CDATA[Jonathan,

Thanks for the comments (and for delaying your response).  

Regarding your one little point, that is a very good point.  I spent little time writing up the review note on that particular item.  I quickly came to the conclusion that there was better than 90% chance that the book author did not realized that all descending indexes are function based indexes and moved on to the next paragraph in the book.  The review is currently 9 typewritten pages in length for the first 150 pages (lots of screen captures and whitespace between paragraphs, so that would only total about 70 pages in another book without all of the unnecessary screen captures).  I am still waiting for my thank you note from the book publisher for the first 21 errata items that I submitted.

I will say that this book author took the time to set up test case scripts, and attempted to show the output of those test case scripts - that is a very big step in the right direction when compared to some of the other books on the market that are targeted at Oracle Database.  The author&#039;s written interpretations of the test case outputs, however, ranged from over-generalizations from a single test result, to vague descriptions of what the results show, to just plain misreading of the test case output.  There are also a couple of test case scripts that test something other than what the author intended to test (the index rebuild test comes to mind).]]></description>
		<content:encoded><![CDATA[<p>Jonathan,</p>
<p>Thanks for the comments (and for delaying your response).  </p>
<p>Regarding your one little point, that is a very good point.  I spent little time writing up the review note on that particular item.  I quickly came to the conclusion that there was better than 90% chance that the book author did not realized that all descending indexes are function based indexes and moved on to the next paragraph in the book.  The review is currently 9 typewritten pages in length for the first 150 pages (lots of screen captures and whitespace between paragraphs, so that would only total about 70 pages in another book without all of the unnecessary screen captures).  I am still waiting for my thank you note from the book publisher for the first 21 errata items that I submitted.</p>
<p>I will say that this book author took the time to set up test case scripts, and attempted to show the output of those test case scripts &#8211; that is a very big step in the right direction when compared to some of the other books on the market that are targeted at Oracle Database.  The author&#8217;s written interpretations of the test case outputs, however, ranged from over-generalizations from a single test result, to vague descriptions of what the results show, to just plain misreading of the test case output.  There are also a couple of test case scripts that test something other than what the author intended to test (the index rebuild test comes to mind).</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/#comment-4456</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Sat, 25 Feb 2012 23:20:45 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6083#comment-4456</guid>
		<description><![CDATA[Jimmy,

Nice test case example.

I displayed the calculated cost column in the execution plan also, using the following syntax:
&lt;pre&gt;
select * from table(dbms_xplan.display_cursor(null,null,&#039;allstats last +cost&#039;));
&lt;/pre&gt;

I experimented a bit with the first half of your test case script.  The full table scan had a calculated cost of 17.  Forcing an index access path with an &lt;b&gt;INDEX(T)&lt;/b&gt; hint resulted in an execution plan that  included and &lt;b&gt;INDEX FULL SCAN&lt;/b&gt; operation with a calculated cost of 28.  Forcing an index skip scan with an &lt;b&gt;INDEX_SS(T)&lt;/b&gt; hint resulted in a calculated cost of 10006 (10005 from the index skip scan operation).

For the second half of your test case script, I obtained a cost of 11 for the INDEX SKIP SCAN access path.  When I forced a full table scan with a &lt;b&gt;FULL(T)&lt;/b&gt; hint, the calcuated cost was 17.

At least in the first 150 pages of the book, there is no mention of the calculated cost of an access path having an influence on the access path that is selected.]]></description>
		<content:encoded><![CDATA[<p>Jimmy,</p>
<p>Nice test case example.</p>
<p>I displayed the calculated cost column in the execution plan also, using the following syntax:</p>
<pre>
select * from table(dbms_xplan.display_cursor(null,null,'allstats last +cost'));
</pre>
<p>I experimented a bit with the first half of your test case script.  The full table scan had a calculated cost of 17.  Forcing an index access path with an <b>INDEX(T)</b> hint resulted in an execution plan that  included and <b>INDEX FULL SCAN</b> operation with a calculated cost of 28.  Forcing an index skip scan with an <b>INDEX_SS(T)</b> hint resulted in a calculated cost of 10006 (10005 from the index skip scan operation).</p>
<p>For the second half of your test case script, I obtained a cost of 11 for the INDEX SKIP SCAN access path.  When I forced a full table scan with a <b>FULL(T)</b> hint, the calcuated cost was 17.</p>
<p>At least in the first 150 pages of the book, there is no mention of the calculated cost of an access path having an influence on the access path that is selected.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonathan Lewis</title>
		<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/#comment-4455</link>
		<dc:creator><![CDATA[Jonathan Lewis]]></dc:creator>
		<pubDate>Sat, 25 Feb 2012 23:10:37 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6083#comment-4455</guid>
		<description><![CDATA[Charles,

I&#039;ve avoided commenting on the quotes because I had assumed you would get some good follow-up. Personally I thought that the quotes showed the type of woolly thinking that is so often displayed in books about Oracle - vague, ambiguous, generalizations based, to some extent, on a kernel of truth that needed a much clearer description.

One little detail, though: the comment about being able to to create descending function-based indexes may be worded badly, but it is describing a significant point. The following statements do have different effects:

[sourcecode]
create index t1_f1 on t1(sqrt(n1));
create index t1_f1 on t1(sqrt(n1) desc));
[/sourcecode]]]></description>
		<content:encoded><![CDATA[<p>Charles,</p>
<p>I&#8217;ve avoided commenting on the quotes because I had assumed you would get some good follow-up. Personally I thought that the quotes showed the type of woolly thinking that is so often displayed in books about Oracle &#8211; vague, ambiguous, generalizations based, to some extent, on a kernel of truth that needed a much clearer description.</p>
<p>One little detail, though: the comment about being able to to create descending function-based indexes may be worded badly, but it is describing a significant point. The following statements do have different effects:</p>
<pre class="brush: plain; title: ; notranslate">
create index t1_f1 on t1(sqrt(n1));
create index t1_f1 on t1(sqrt(n1) desc));
</pre>
]]></content:encoded>
	</item>
	<item>
		<title>By: jimmyb</title>
		<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/#comment-4454</link>
		<dc:creator><![CDATA[jimmyb]]></dc:creator>
		<pubDate>Sat, 25 Feb 2012 21:58:58 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6083#comment-4454</guid>
		<description><![CDATA[I used 11.2.0.2 to test the INDEX SKIP SCAN or INDEX FAST FULL SCAN issue.

&lt;pre&gt;
SQL&gt; create table t
 ( id   number ,
   code number ,
   txt  varchar2(25)
 )
 / 

Table created.
 
 SQL&gt; insert into t
 select   rownum
        , MOD(ROWNUM,100)
        , dbms_random.string(&#039;A&#039;,25)
 from
          dual
 connect by level &lt; 10000;
 
commit;

Commit complete.

SQL&gt; create index t_idx on t(id,code) ;

Index created.

SQL&gt; exec dbms_stats.gather_table_stats(user,&#039;T&#039;)

PL/SQL procedure successfully completed.

SQL&gt; alter session set statistics_level=all;

Session altered.

SQL&gt; set serveroutput off

SQL&gt; select * from t where code = 1;

100 rows selected.

SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,&#039;allstats last&#039;));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  c6mtjhmq58ng4, child number 0
-------------------------------------
select * from t where code = 1

Plan hash value: 1601196873

------------------------------------------------------------------------------------
&#124; Id  &#124; Operation         &#124; Name &#124; Starts &#124; E-Rows &#124; A-Rows &#124;   A-Time   &#124; Buffers &#124;
------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT  &#124;      &#124;      1 &#124;        &#124;    100 &#124;00:00:00.01 &#124;70 &#124;
&#124;*  1 &#124;  TABLE ACCESS FULL&#124; T    &#124;      1 &#124;    100 &#124;    100 &#124;00:00:00.01 &#124;70 &#124;
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(&quot;CODE&quot;=1)

-- This is what I expected - FTS. Now let&#039;s only reference the indexed columns in the select clause
SQL&gt; select id, code from t where code = 1;

100 rows selected.

SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,&#039;allstats last&#039;));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  718d4y9b3fqtz, child number 1
-------------------------------------
update CRC$_RESULT_CACHE_STATS                   
    set NAME = :1, 
        VALUE = :2 
    where CACHE_ID = :3 
    and   STAT_ID = :4

Plan hash value: 1807565214

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------
&#124; Id  &#124; Operation          &#124; Name                    &#124; Starts &#124; E-Rows &#124; A-Rows&#124;   A-Time   &#124; Buffers &#124;
--------------------------------------------------------------------------------------------------------
&#124;   0 &#124; UPDATE STATEMENT   &#124;                         &#124;     10 &#124;        &#124;      0&#124;00:00:00.01 &#124;      21 &#124;
&#124;   1 &#124;  UPDATE            &#124; CRC$_RESULT_CACHE_STATS &#124;     10 &#124;        &#124;      0&#124;00:00:00.01 &#124;      21 &#124;
&#124;*  2 &#124;   INDEX UNIQUE SCAN&#124; CRC$_RCSTATSPK          &#124;     10 &#124;      1 &#124;     10&#124;00:00:00.01 &#124;      10 &#124;
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(&quot;CACHE_ID&quot;=:3 AND &quot;STAT_ID&quot;=:4)

-- Oracle updates the the CLIENT RESULT CACHE
-- If we don&#039;t modify the table data the Optimizer should be able to use this view

SQL&gt; select id, code from t where code = 1;

100 rows selected.

SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,&#039;allstats last&#039;));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  dhv1zshgrubgv, child number 0
-------------------------------------
select id, code from t where code = 1

Plan hash value: 3163761342

----------------------------------------------------------------------------------------
&#124; Id  &#124; Operation            &#124; Name  &#124; Starts &#124; E-Rows &#124; A-Rows &#124;   A-Time   &#124; Buffers &#124;
----------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT     &#124;       &#124;      1 &#124;        &#124;    100 &#124;00:00:00.01 &#124;    38 &#124;
&#124;*  1 &#124;  INDEX FAST FULL SCAN&#124; T_IDX &#124;      1 &#124;    100 &#124;    100 &#124;00:00:00.01 &#124;    38 &#124;
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(&quot;CODE&quot;=1)

-- This makes sense. When all of the columns in a select cause can be satisfied Oracle is able to a INDEX FAST FULL SCAN

-- ############NOW LET&#039;s DROP and RECREATE THE TABLE SO the LEADING COLUMN HAS A LOWER CARDINALITY. AS SOMEONE PROVED EARLIER THIS SHOULD GET AN INDEX SKIP SCAN

SQL&gt; drop table t purge;

Table dropped.
 
SQL&gt; create table t
 ( id   number ,
   code number ,
   txt  varchar2(25)
 )
 / 

Table created.

SQL&gt; insert into t
 select   TRUNC(DBMS_RANDOM.VALUE(0, 9))
        , ROWNUM
        , dbms_random.string(&#039;A&#039;,25)
 from
          dual
 connect by level &lt; 10000;
 
commit;

Commit complete.

SQL&gt; select distinct id from t;

        ID
----------
         1
         6
         2
         5
         4
         8
         3
         7
         0

9 rows selected.

-- ######SO THE LEADING COLUMN IN OUR COMPOSITE INDEX ONLY HAS 9 DISTINCT VALUES

SQL&gt; create index t_idx on t(id,code);

Index created.

SQL&gt; exec dbms_stats.gather_table_stats(user,&#039;T&#039;)

PL/SQL procedure successfully completed.

SQL&gt; select * from t where code = 90;

        ID       CODE TXT
---------- ---------- -------------------------
         2         90 YMZUHsDykWENIyGekAtrQCqBZ

SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,&#039;allstats last&#039;));

Plan hash value: 2053318169

-----------------------------------------------------------------------------------------------
&#124; Id  &#124; Operation                   &#124; Name  &#124; Starts &#124; E-Rows &#124; A-Rows &#124;   A-Time   &#124; Buffers &#124;
-----------------------------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT            &#124;       &#124;      1 &#124;        &#124;      1 &#124;00:00:00.01 &#124;      14 &#124;
&#124;   1 &#124;  TABLE ACCESS BY INDEX ROWID&#124; T     &#124;      1 &#124;      1 &#124;      1 &#124;00:00:00.01 &#124;      14 &#124;
&#124;*  2 &#124;   INDEX SKIP SCAN           &#124; T_IDX &#124;      1 &#124;      1 &#124;      1 &#124;00:00:00.01 &#124;      13 &#124;
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access(&quot;CODE&quot;=90)
       filter(&quot;CODE&quot;=90)
&lt;/pre&gt;]]></description>
		<content:encoded><![CDATA[<p>I used 11.2.0.2 to test the INDEX SKIP SCAN or INDEX FAST FULL SCAN issue.</p>
<pre>
SQL&gt; create table t
 ( id   number ,
   code number ,
   txt  varchar2(25)
 )
 / 

Table created.
 
 SQL&gt; insert into t
 select   rownum
        , MOD(ROWNUM,100)
        , dbms_random.string('A',25)
 from
          dual
 connect by level &lt; 10000;
 
commit;

Commit complete.

SQL&gt; create index t_idx on t(id,code) ;

Index created.

SQL&gt; exec dbms_stats.gather_table_stats(user,'T')

PL/SQL procedure successfully completed.

SQL&gt; alter session set statistics_level=all;

Session altered.

SQL&gt; set serveroutput off

SQL&gt; select * from t where code = 1;

100 rows selected.

SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  c6mtjhmq58ng4, child number 0
-------------------------------------
select * from t where code = 1

Plan hash value: 1601196873

------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |    100 |00:00:00.01 |70 |
|*  1 |  TABLE ACCESS FULL| T    |      1 |    100 |    100 |00:00:00.01 |70 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("CODE"=1)

-- This is what I expected - FTS. Now let's only reference the indexed columns in the select clause
SQL&gt; select id, code from t where code = 1;

100 rows selected.

SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  718d4y9b3fqtz, child number 1
-------------------------------------
update CRC$_RESULT_CACHE_STATS                   
    set NAME = :1, 
        VALUE = :2 
    where CACHE_ID = :3 
    and   STAT_ID = :4

Plan hash value: 1807565214

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name                    | Starts | E-Rows | A-Rows|   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT   |                         |     10 |        |      0|00:00:00.01 |      21 |
|   1 |  UPDATE            | CRC$_RESULT_CACHE_STATS |     10 |        |      0|00:00:00.01 |      21 |
|*  2 |   INDEX UNIQUE SCAN| CRC$_RCSTATSPK          |     10 |      1 |     10|00:00:00.01 |      10 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("CACHE_ID"=:3 AND "STAT_ID"=:4)

-- Oracle updates the the CLIENT RESULT CACHE
-- If we don't modify the table data the Optimizer should be able to use this view

SQL&gt; select id, code from t where code = 1;

100 rows selected.

SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  dhv1zshgrubgv, child number 0
-------------------------------------
select id, code from t where code = 1

Plan hash value: 3163761342

----------------------------------------------------------------------------------------
| Id  | Operation            | Name  | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |       |      1 |        |    100 |00:00:00.01 |    38 |
|*  1 |  INDEX FAST FULL SCAN| T_IDX |      1 |    100 |    100 |00:00:00.01 |    38 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("CODE"=1)

-- This makes sense. When all of the columns in a select cause can be satisfied Oracle is able to a INDEX FAST FULL SCAN

-- ############NOW LET's DROP and RECREATE THE TABLE SO the LEADING COLUMN HAS A LOWER CARDINALITY. AS SOMEONE PROVED EARLIER THIS SHOULD GET AN INDEX SKIP SCAN

SQL&gt; drop table t purge;

Table dropped.
 
SQL&gt; create table t
 ( id   number ,
   code number ,
   txt  varchar2(25)
 )
 / 

Table created.

SQL&gt; insert into t
 select   TRUNC(DBMS_RANDOM.VALUE(0, 9))
        , ROWNUM
        , dbms_random.string('A',25)
 from
          dual
 connect by level &lt; 10000;
 
commit;

Commit complete.

SQL&gt; select distinct id from t;

        ID
----------
         1
         6
         2
         5
         4
         8
         3
         7
         0

9 rows selected.

-- ######SO THE LEADING COLUMN IN OUR COMPOSITE INDEX ONLY HAS 9 DISTINCT VALUES

SQL&gt; create index t_idx on t(id,code);

Index created.

SQL&gt; exec dbms_stats.gather_table_stats(user,'T')

PL/SQL procedure successfully completed.

SQL&gt; select * from t where code = 90;

        ID       CODE TXT
---------- ---------- -------------------------
         2         90 YMZUHsDykWENIyGekAtrQCqBZ

SQL&gt; select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

Plan hash value: 2053318169

-----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |      1 |        |      1 |00:00:00.01 |      14 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |      1 |      1 |      1 |00:00:00.01 |      14 |
|*  2 |   INDEX SKIP SCAN           | T_IDX |      1 |      1 |      1 |00:00:00.01 |      13 |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("CODE"=90)
       filter("CODE"=90)
</pre>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/#comment-4453</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Sat, 25 Feb 2012 19:29:53 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6083#comment-4453</guid>
		<description><![CDATA[The number of comments seems to have slowed for this blog article, so I thought that I would post my book review notes that are related to the above quotes.  My book review notes will likely miss some of the points made in the comments above, however, this page will be referenced at the bottom of the completed book review.

#1: The book casually demonstrates setting up a 16KB block size tablespace in a database that has a default 8KB block size.  The book provides several advantages for including smaller or larger than default block sizes in a single database including, “Faster scans: tables and indexes that require full scans can see faster performance when placed in a large block size.”  This justification is incorrect for several reasons including the fact that the DB_FILE_MULTIBLOCK_READ_COUNT parameter is scaled accordingly for tablespaces that use a smaller than database default block size, and scales the parameter down for tablespaces that use a larger than database default block size.  All of the justifications found on page 88 &lt;i&gt;(temporarily hidden, but the justifications remind me of a very long OTN thread from May/June 2008 that was on a related topic)&lt;/i&gt;.  The book does not discuss the bugs and unexpected optimizer cost changes that might result from using multiple block sizes in a single database

#2: The book states, “We can also create a function-based descending index.”  This is a strange statement – all descending indexes in Oracle Database are function-based indexes.

#3: The book states, “… this test allows us to dispel a myth. Oracle uses the indexes even if the leading columns are not referenced in the WHERE predicate of the query.  We can see that in such a case, the operation will be an INDEX FAST FULL SCAN.”  In this case, the author is incorrectly attempting to generalize a special case into a general rule.  Firstly, there is no myth to dispel – Oracle’s query optimizer has had the ability to use INDEX SKIP SCAN operations when the leading column of an index is not specified in the WHERE clause, since the release of Oracle Database 9.0.1 a decade ago – but that access path is usually only advisable when there are few distinct values in the leading column of the index.  The author’s test case is a special case because all of the columns selected from the table are present in the index structure. 

#4: The book states, “If we use a regular index to access the data, Oracle is unable to do the sort in a mixed way, in a query like this.”  The author then shows a SQL statement with the first column in the ORDER BY clause sorted in descending order and the second column in the ORDER BY clause sorted in ascending order.  At this point in the book, the author has not yet stated that Oracle Database is able to read index entries in an ascending or descending order through a normal (ascending sorted) b*tree index, so this sentence in the book is confusing – almost to say that Oracle Database is not able to sort one column in ascending sequence and a second column in descending sequence – that concept is obviously false.  It would have been more accurate for the book to state that, “Oracle is unable to _avoid_ a sort operation when accessing the rows through a concatenated index if both of the columns in the index are sorted in ascending sequence, the ORDER BY clause of the SQL statement specifies that one and only one column contained in the index should be ordered in descending sequence, and the second column in the concatenated index is included in the WHERE clause.”

#5: •	The book states, “However, when we have a table on which there are many INSERTs and DELETEs, we could schedule an index rebuild, because when deleting an index entry, the space is not freed in the index leaf, but just marked as deleted. If we have massive DELETE and INSERT operations, we could have a skewed index structure, which could slow performance due to intra-block fragmentation.”  The book should have defined what is meant by “skewed index structure” – does the book mean, for instance, that one portion of the index could have a BLEVEL of 2 while another portion of the index could have a BLEVEL of 3 – if that is the case, the book’s statement is incorrect.  If the book’s definition of “skewed index structure” is that some leaf blocks of the index will be more densely packed than other leaf blocks in the same index structure, then that should be considered normal behavior for Oracle indexes – an occasional coalesce might be used to combine index entries in logically adjacent leaf blocks, but scheduling index rebuilds is neither required nor recommended.  Depending on the order of the inserted values in relation to the order of the entries in the index leaf blocks, an index leaf block split operation could evenly divide the existing index entries between two leaf blocks (a 50-50 split, resulting in both index blocks being 50% utilized, if the inserted value is not the highest value that would be inserted into the leaf block), or all of the existing entries will remain in the existing leaf block and the new entry will be placed by itself into a new leaf block (a 90-10 split).  A deleted index entry will remain in the block at least until that transaction is committed, but any post-transaction insert into the block will clear out all deleted index entries in the block.  Deleting all table rows with index entries at the low end of the index (the values were populated by a sequence, for example, and are deleted in the same sequential order) could leave many blocks in the index structure with nothing but deleted index entries, but that situation should only result in a performance problem if SQL statements attempt to determine the minimum value for the indexed column, or to some extent, fast full index scans and full index scans (reference http://richardfoote.wordpress.com/2011/05/22/del_lf_rows-index-rebuild-criteria-codex/ http://hoopercharles.wordpress.com/2009/12/06/determining-why-a-query-using-mincolumn-in-the-where-clause-on-an-indexed-column-takes-a-long-time/ ). &lt;i&gt;See the test case above.&lt;/i&gt;

#6.5 The book states, “Bitmap indexes offer very fast performance when we have a low cardinality field indexed on a table containing many rows.”  This statement could have several different interpretations, but I believe that the author’s intended meaning is “Bitmap indexes offer significantly faster performance than b*tree indexes when columns with few distinct values are indexed in tables containing a significant number of rows.”  This fixed statement still requires additional clarification – if the bitmap index does not help to further reduce the number of table rows that are accessed through the index, the end result may be performance that is roughly the same as that of b*tree indexes.  One way to accomplish the task of further reducing the number of table rows accessed is through the utilization of multiple bitmap indexes with bitmap combine operations to significantly reduce the number of rowids that are used to fetch table rows (page 139).

#7: The book states, “When rows are frequently inserted, deleted, and updated, there is a performance bottleneck if we use a bitmap index. When the index is updated, all the bitmap segments are locked.”  This statement requires a bit of clarification.  I do not believe that the author is stating that updating an entry in a bitmap index will lock all of the bitmap indexes in the database (a segment could be a table, table partition, index, etc.).  Instead, I think that the author is intending to state that updating an entry in a bitmap index will lock all of the index entries in that index, effectively preventing any other session from inserting, updating (the column covered by the index), or deleting rows in the table.  For very small bitmap indexes, this statement could very well be true.  However, for larger bitmap indexes, built for tables with many rows, the number of index rows that will be locked during an update is determined by the number of rows covered by the index block(s) that update changed, possibly 20,000 to 50,000 rows per index block. (page 139 reference: http://www.juliandyke.com/Presentations/BitmapIndexInternals.ppt slide 46, http://www.jlcomp.demon.co.uk/03_bitmap_1.doc page 2, http://laurentschneider.com/wordpress/2007/03/why-is-bitmap-index-not-designed-for-oltp.html comments section)]]></description>
		<content:encoded><![CDATA[<p>The number of comments seems to have slowed for this blog article, so I thought that I would post my book review notes that are related to the above quotes.  My book review notes will likely miss some of the points made in the comments above, however, this page will be referenced at the bottom of the completed book review.</p>
<p>#1: The book casually demonstrates setting up a 16KB block size tablespace in a database that has a default 8KB block size.  The book provides several advantages for including smaller or larger than default block sizes in a single database including, “Faster scans: tables and indexes that require full scans can see faster performance when placed in a large block size.”  This justification is incorrect for several reasons including the fact that the DB_FILE_MULTIBLOCK_READ_COUNT parameter is scaled accordingly for tablespaces that use a smaller than database default block size, and scales the parameter down for tablespaces that use a larger than database default block size.  All of the justifications found on page 88 <i>(temporarily hidden, but the justifications remind me of a very long OTN thread from May/June 2008 that was on a related topic)</i>.  The book does not discuss the bugs and unexpected optimizer cost changes that might result from using multiple block sizes in a single database</p>
<p>#2: The book states, “We can also create a function-based descending index.”  This is a strange statement – all descending indexes in Oracle Database are function-based indexes.</p>
<p>#3: The book states, “… this test allows us to dispel a myth. Oracle uses the indexes even if the leading columns are not referenced in the WHERE predicate of the query.  We can see that in such a case, the operation will be an INDEX FAST FULL SCAN.”  In this case, the author is incorrectly attempting to generalize a special case into a general rule.  Firstly, there is no myth to dispel – Oracle’s query optimizer has had the ability to use INDEX SKIP SCAN operations when the leading column of an index is not specified in the WHERE clause, since the release of Oracle Database 9.0.1 a decade ago – but that access path is usually only advisable when there are few distinct values in the leading column of the index.  The author’s test case is a special case because all of the columns selected from the table are present in the index structure. </p>
<p>#4: The book states, “If we use a regular index to access the data, Oracle is unable to do the sort in a mixed way, in a query like this.”  The author then shows a SQL statement with the first column in the ORDER BY clause sorted in descending order and the second column in the ORDER BY clause sorted in ascending order.  At this point in the book, the author has not yet stated that Oracle Database is able to read index entries in an ascending or descending order through a normal (ascending sorted) b*tree index, so this sentence in the book is confusing – almost to say that Oracle Database is not able to sort one column in ascending sequence and a second column in descending sequence – that concept is obviously false.  It would have been more accurate for the book to state that, “Oracle is unable to _avoid_ a sort operation when accessing the rows through a concatenated index if both of the columns in the index are sorted in ascending sequence, the ORDER BY clause of the SQL statement specifies that one and only one column contained in the index should be ordered in descending sequence, and the second column in the concatenated index is included in the WHERE clause.”</p>
<p>#5: •	The book states, “However, when we have a table on which there are many INSERTs and DELETEs, we could schedule an index rebuild, because when deleting an index entry, the space is not freed in the index leaf, but just marked as deleted. If we have massive DELETE and INSERT operations, we could have a skewed index structure, which could slow performance due to intra-block fragmentation.”  The book should have defined what is meant by “skewed index structure” – does the book mean, for instance, that one portion of the index could have a BLEVEL of 2 while another portion of the index could have a BLEVEL of 3 – if that is the case, the book’s statement is incorrect.  If the book’s definition of “skewed index structure” is that some leaf blocks of the index will be more densely packed than other leaf blocks in the same index structure, then that should be considered normal behavior for Oracle indexes – an occasional coalesce might be used to combine index entries in logically adjacent leaf blocks, but scheduling index rebuilds is neither required nor recommended.  Depending on the order of the inserted values in relation to the order of the entries in the index leaf blocks, an index leaf block split operation could evenly divide the existing index entries between two leaf blocks (a 50-50 split, resulting in both index blocks being 50% utilized, if the inserted value is not the highest value that would be inserted into the leaf block), or all of the existing entries will remain in the existing leaf block and the new entry will be placed by itself into a new leaf block (a 90-10 split).  A deleted index entry will remain in the block at least until that transaction is committed, but any post-transaction insert into the block will clear out all deleted index entries in the block.  Deleting all table rows with index entries at the low end of the index (the values were populated by a sequence, for example, and are deleted in the same sequential order) could leave many blocks in the index structure with nothing but deleted index entries, but that situation should only result in a performance problem if SQL statements attempt to determine the minimum value for the indexed column, or to some extent, fast full index scans and full index scans (reference <a href="http://richardfoote.wordpress.com/2011/05/22/del_lf_rows-index-rebuild-criteria-codex/" rel="nofollow">http://richardfoote.wordpress.com/2011/05/22/del_lf_rows-index-rebuild-criteria-codex/</a> <a href="http://hoopercharles.wordpress.com/2009/12/06/determining-why-a-query-using-mincolumn-in-the-where-clause-on-an-indexed-column-takes-a-long-time/" rel="nofollow">http://hoopercharles.wordpress.com/2009/12/06/determining-why-a-query-using-mincolumn-in-the-where-clause-on-an-indexed-column-takes-a-long-time/</a> ). <i>See the test case above.</i></p>
<p>#6.5 The book states, “Bitmap indexes offer very fast performance when we have a low cardinality field indexed on a table containing many rows.”  This statement could have several different interpretations, but I believe that the author’s intended meaning is “Bitmap indexes offer significantly faster performance than b*tree indexes when columns with few distinct values are indexed in tables containing a significant number of rows.”  This fixed statement still requires additional clarification – if the bitmap index does not help to further reduce the number of table rows that are accessed through the index, the end result may be performance that is roughly the same as that of b*tree indexes.  One way to accomplish the task of further reducing the number of table rows accessed is through the utilization of multiple bitmap indexes with bitmap combine operations to significantly reduce the number of rowids that are used to fetch table rows (page 139).</p>
<p>#7: The book states, “When rows are frequently inserted, deleted, and updated, there is a performance bottleneck if we use a bitmap index. When the index is updated, all the bitmap segments are locked.”  This statement requires a bit of clarification.  I do not believe that the author is stating that updating an entry in a bitmap index will lock all of the bitmap indexes in the database (a segment could be a table, table partition, index, etc.).  Instead, I think that the author is intending to state that updating an entry in a bitmap index will lock all of the index entries in that index, effectively preventing any other session from inserting, updating (the column covered by the index), or deleting rows in the table.  For very small bitmap indexes, this statement could very well be true.  However, for larger bitmap indexes, built for tables with many rows, the number of index rows that will be locked during an update is determined by the number of rows covered by the index block(s) that update changed, possibly 20,000 to 50,000 rows per index block. (page 139 reference: <a href="http://www.juliandyke.com/Presentations/BitmapIndexInternals.ppt" rel="nofollow">http://www.juliandyke.com/Presentations/BitmapIndexInternals.ppt</a> slide 46, <a href="http://www.jlcomp.demon.co.uk/03_bitmap_1.doc" rel="nofollow">http://www.jlcomp.demon.co.uk/03_bitmap_1.doc</a> page 2, <a href="http://laurentschneider.com/wordpress/2007/03/why-is-bitmap-index-not-designed-for-oltp.html" rel="nofollow">http://laurentschneider.com/wordpress/2007/03/why-is-bitmap-index-not-designed-for-oltp.html</a> comments section)</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Mich talebzadeh</title>
		<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/#comment-4452</link>
		<dc:creator><![CDATA[Mich talebzadeh]]></dc:creator>
		<pubDate>Fri, 24 Feb 2012 15:14:38 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6083#comment-4452</guid>
		<description><![CDATA[Hi Charles,

I wish I could dig that old stuff from 10g but that was 6 years ago.

However, I was doing this test on SSD response recently and I recall the following tests 

First increase the number of rows in t5 to 12 million

&lt;pre&gt;
exec populate_t5_sp(12000000)

PL/SQL procedure successfully completed.
&lt;/pre&gt;

and then I ran the follwing SQL

&lt;pre&gt;
  1  SELECT count(1)
  2  FROM T5
  3  where   n1 &lt;= 8
  4  and     ind_pad &lt;= rpad(&#039;x&#039;,39)&#124;&#124;&#039;x&#039;
  5* and     n2       /


Execution Plan
----------------------------------------------------------
Plan hash value: 2226148900

--------------------------------------------------------------------------
&#124; Id  &#124; Operation        &#124; Name  &#124; Rows  &#124; Bytes &#124; Cost (%CPU)&#124; Time     &#124;
--------------------------------------------------------------------------
&#124;   0 &#124; SELECT STATEMENT &#124;       &#124;     1 &#124;    47 &#124; 35558   (1)&#124; 00:07:07 &#124;
&#124;   1 &#124;  SORT AGGREGATE  &#124;       &#124;     1 &#124;    47 &#124;            &#124;          &#124;
&#124;*  2 &#124;   INDEX SKIP SCAN&#124; T5_I1 &#124;  3064K&#124;   137M&#124; 35558   (1)&#124; 00:07:07 &#124;
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(&quot;N1&quot;&lt;=8 AND &quot;IND_PAD&quot;&lt;=&#039;x
                   x&#039; AND &quot;N2&quot;&lt;15)
       filter(&quot;N2&quot;&lt;15 AND &quot;IND_PAD&quot;&lt;=&#039;x
                   x&#039;)


Statistics
----------------------------------------------------------
        384  recursive calls
          0  db block gets
      38124  consistent gets
          0  physical reads
          0  redo size
        529  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          9  sorts (memory)
          0  sorts (disk)
          1  rows processed
&lt;/pre&gt;
I believe the reason the optimizer is using the index is due to 1)  access via the leading column N1 and 2) filtering via N2. When I discussed the result of this test (dealing with SSD), with Tom Kyte he actually remarked calling it &quot;a funky query plan ..... - Why introduce a relatively rare index skip scan into the mix?&quot; 

The important thing is the cardnilaity of leading column. So if we have a leading column with few distinct values (in this case n1 has 25 values), then the index may be used pretty efficiently by probing it 25 times. On the other hand, if the leading column has relatively high cardinality then an Index Skip Scan is not a viable option. 

As a sideline Sybase calls it &quot;intelligent index scan&quot; with the trace output referring to it as &quot;Intelligent Scan selectivity reduction&quot;.

Poor DBAs have to remember all these :-)

Cheers,

Mich]]></description>
		<content:encoded><![CDATA[<p>Hi Charles,</p>
<p>I wish I could dig that old stuff from 10g but that was 6 years ago.</p>
<p>However, I was doing this test on SSD response recently and I recall the following tests </p>
<p>First increase the number of rows in t5 to 12 million</p>
<pre>
exec populate_t5_sp(12000000)

PL/SQL procedure successfully completed.
</pre>
<p>and then I ran the follwing SQL</p>
<pre>
  1  SELECT count(1)
  2  FROM T5
  3  where   n1 &lt;= 8
  4  and     ind_pad &lt;= rpad(&#039;x&#039;,39)||&#039;x&#039;
  5* and     n2       /


Execution Plan
----------------------------------------------------------
Plan hash value: 2226148900

--------------------------------------------------------------------------
| Id  | Operation        | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT |       |     1 |    47 | 35558   (1)| 00:07:07 |
|   1 |  SORT AGGREGATE  |       |     1 |    47 |            |          |
|*  2 |   INDEX SKIP SCAN| T5_I1 |  3064K|   137M| 35558   (1)| 00:07:07 |
--------------------------------------------------------------------------

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

   2 - access("N1"&lt;=8 AND &quot;IND_PAD&quot;&lt;=&#039;x
                   x&#039; AND &quot;N2&quot;&lt;15)
       filter(&quot;N2&quot;&lt;15 AND &quot;IND_PAD&quot;&lt;=&#039;x
                   x&#039;)


Statistics
----------------------------------------------------------
        384  recursive calls
          0  db block gets
      38124  consistent gets
          0  physical reads
          0  redo size
        529  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          9  sorts (memory)
          0  sorts (disk)
          1  rows processed
</pre>
<p>I believe the reason the optimizer is using the index is due to 1)  access via the leading column N1 and 2) filtering via N2. When I discussed the result of this test (dealing with SSD), with Tom Kyte he actually remarked calling it &quot;a funky query plan &#8230;.. &#8211; Why introduce a relatively rare index skip scan into the mix?&quot; </p>
<p>The important thing is the cardnilaity of leading column. So if we have a leading column with few distinct values (in this case n1 has 25 values), then the index may be used pretty efficiently by probing it 25 times. On the other hand, if the leading column has relatively high cardinality then an Index Skip Scan is not a viable option. </p>
<p>As a sideline Sybase calls it &quot;intelligent index scan&quot; with the trace output referring to it as &quot;Intelligent Scan selectivity reduction&quot;.</p>
<p>Poor DBAs have to remember all these <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Cheers,</p>
<p>Mich</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2012/02/22/interesting-index-facts-what-is-wrong-with-these-quotes/#comment-4451</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Fri, 24 Feb 2012 13:37:06 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6083#comment-4451</guid>
		<description><![CDATA[Let&#039;s take a closer look at the rebuild criteria offered in the book.

First, create a test table, populate that table, generate an entry in INDEX_STATS for the index, and display some of the statistics from INDEX_STATS:
&lt;pre&gt;
DROP TABLE T1 PURGE;
 
CREATE TABLE T1 AS
SELECT
  ROWNUM C1,
  RPAD(&#039;A&#039;,200,&#039;A&#039;) C2
FROM
  DUAL
CONNECT BY
  LEVEL&lt;=10000;
 
CREATE INDEX IND_T1_C1 ON T1(C1);
 
ANALYZE INDEX IND_T1_C1 VALIDATE STRUCTURE;
 
SELECT
  LF_ROWS,
  DEL_LF_ROWS,
  HEIGHT,
  LF_BLKS,
  ROUND(DEL_LF_ROWS/LF_ROWS*100,2) PERCENT
FROM
  INDEX_STATS;
 
LF_ROWS DEL_LF_ROWS     HEIGHT    LF_BLKS    PERCENT
------- ----------- ---------- ---------- ----------
  10000           0          2         21          0
&lt;/pre&gt;
There are currently no deleted index entries, and because the index entries were added in sequential order, 90-10 leaf block splits were used.  Currently, there are 10,000 rows in the index leafs, 0 rows are deleted, the the height is 2, there are 21 leaf blocks, and 0% of the rows in the index structure are marked as deleted.  Based on what the book states, we do not need to rebuild this index.

Continuing the test, we will delete all rows from the index:
&lt;pre&gt;
DELETE FROM T1;
 
ANALYZE INDEX IND_T1_C1 VALIDATE STRUCTURE;
 
SELECT
  LF_ROWS,
  DEL_LF_ROWS,
  HEIGHT,
  LF_BLKS,
  ROUND(DEL_LF_ROWS/LF_ROWS*100,2) PERCENT
FROM
  INDEX_STATS;
 
LF_ROWS DEL_LF_ROWS     HEIGHT    LF_BLKS    PERCENT
------- ----------- ---------- ---------- ----------
  10000       10000          2         21        100
&lt;/pre&gt;
There are currently 10,000 rows in the index leafs, 10,000 of those rows are deleted, the height is 2, there are 21 leaf blocks, and 100% of the rows in the index structure are marked as deleted.  Based on what the book states, we do not need to rebuild this index.  DEL_LF_ROWS/LF_ROWS = 1, which is not greater than 2 (this cannot ever be the case), LF_ROWS is not lower than LF_BLKS (this can never be the case, the block will be detached from the index structure when it is entirely empty), and HEIGHT is not 4 (why 4, does that mean that 2, 3, 5,7, etc. are OK?).
 
Continuing the test, we will insert a single row into the table, and repopulate INDEX_STATS for the index:
&lt;pre&gt;
INSERT INTO T1 VALUES (1,&#039;A&#039;);
 
COMMIT;
 
ANALYZE INDEX IND_T1_C1 VALIDATE STRUCTURE;
 
SELECT
  LF_ROWS,
  DEL_LF_ROWS,
  HEIGHT,
  LF_BLKS,
  ROUND(DEL_LF_ROWS/LF_ROWS*100,2) PERCENT
FROM
  INDEX_STATS;
 
LF_ROWS DEL_LF_ROWS     HEIGHT    LF_BLKS    PERCENT
------- ----------- ---------- ---------- ----------
   9516        9515          2         21      99.99
&lt;/pre&gt;
As shown above, inserting a single row into the table cleared out 485 deleted rows (plus 1 for the inserted row) from the index leafs.  Based on what the book states, we do not need to rebuild this index.  DEL_LF_ROWS/LF_ROWS = 0.9999, which is not greater than 2.
 
Continuing the test, inserting a row with a value that is 1 greater than the previously deleted maximum value:
&lt;pre&gt;
INSERT INTO T1 VALUES (10001,&#039;A&#039;);
 
COMMIT;
 
ANALYZE INDEX IND_T1_C1 VALIDATE STRUCTURE;
 
SELECT
  LF_ROWS,
  DEL_LF_ROWS,
  HEIGHT,
  LF_BLKS,
  ROUND(DEL_LF_ROWS/LF_ROWS*100,2) PERCENT
FROM
  INDEX_STATS;
 
LF_ROWS DEL_LF_ROWS     HEIGHT    LF_BLKS    PERCENT
------- ----------- ---------- ---------- ----------
   9103        9101          2         21      99.98
&lt;/pre&gt;
As shown above, inserting a single row into the table cleared out an additional 414 deleted rows (plus 1 for the inserted row) from the index leafs.  Based on what the book states, we do not need to rebuild this index.  DEL_LF_ROWS/LF_ROWS = 0.9998, which is not greater than 2.

Continuing the test, inserting 100 rows into the table, with the values spaced 100 apart (100, 200, 300, etc.):
&lt;pre&gt;
INSERT INTO
  T1 
SELECT
  ROWNUM*100 C1,
  RPAD(&#039;A&#039;,200,&#039;A&#039;) C2
FROM
  DUAL
CONNECT BY
  LEVEL&lt;=100;
 
COMMIT;
 
ANALYZE INDEX IND_T1_C1 VALIDATE STRUCTURE;
 
SELECT
  LF_ROWS,
  DEL_LF_ROWS,
  HEIGHT,
  LF_BLKS,
  ROUND(DEL_LF_ROWS/LF_ROWS*100,2) PERCENT
FROM
  INDEX_STATS;
 
LF_ROWS DEL_LF_ROWS     HEIGHT    LF_BLKS    PERCENT
------- ----------- ---------- ---------- ----------
    102           0          2         21          0
&lt;/pre&gt;
As shown above, inserting 100 rows into the table cleared out 9101 additional deleted rows (plus 100 for the inserted rowed) from the index leafs.  Based on what the book states, we do not need to rebuild this index.  DEL_LF_ROWS/LF_ROWS = 0.0000, which is not greater than 2.

This index is now sparsely populated with on average less than 5 rows per leaf block, when it started out with an average of 476.19 rows per leaf block.  Should this index be rebuilt?  I suppose that depends on how the index will be used in the future, maybe a coalesce is a better choice, maybe doing nothing is a better choice.]]></description>
		<content:encoded><![CDATA[<p>Let&#8217;s take a closer look at the rebuild criteria offered in the book.</p>
<p>First, create a test table, populate that table, generate an entry in INDEX_STATS for the index, and display some of the statistics from INDEX_STATS:</p>
<pre>
DROP TABLE T1 PURGE;
 
CREATE TABLE T1 AS
SELECT
  ROWNUM C1,
  RPAD('A',200,'A') C2
FROM
  DUAL
CONNECT BY
  LEVEL&lt;=10000;
 
CREATE INDEX IND_T1_C1 ON T1(C1);
 
ANALYZE INDEX IND_T1_C1 VALIDATE STRUCTURE;
 
SELECT
  LF_ROWS,
  DEL_LF_ROWS,
  HEIGHT,
  LF_BLKS,
  ROUND(DEL_LF_ROWS/LF_ROWS*100,2) PERCENT
FROM
  INDEX_STATS;
 
LF_ROWS DEL_LF_ROWS     HEIGHT    LF_BLKS    PERCENT
------- ----------- ---------- ---------- ----------
  10000           0          2         21          0
</pre>
<p>There are currently no deleted index entries, and because the index entries were added in sequential order, 90-10 leaf block splits were used.  Currently, there are 10,000 rows in the index leafs, 0 rows are deleted, the the height is 2, there are 21 leaf blocks, and 0% of the rows in the index structure are marked as deleted.  Based on what the book states, we do not need to rebuild this index.</p>
<p>Continuing the test, we will delete all rows from the index:</p>
<pre>
DELETE FROM T1;
 
ANALYZE INDEX IND_T1_C1 VALIDATE STRUCTURE;
 
SELECT
  LF_ROWS,
  DEL_LF_ROWS,
  HEIGHT,
  LF_BLKS,
  ROUND(DEL_LF_ROWS/LF_ROWS*100,2) PERCENT
FROM
  INDEX_STATS;
 
LF_ROWS DEL_LF_ROWS     HEIGHT    LF_BLKS    PERCENT
------- ----------- ---------- ---------- ----------
  10000       10000          2         21        100
</pre>
<p>There are currently 10,000 rows in the index leafs, 10,000 of those rows are deleted, the height is 2, there are 21 leaf blocks, and 100% of the rows in the index structure are marked as deleted.  Based on what the book states, we do not need to rebuild this index.  DEL_LF_ROWS/LF_ROWS = 1, which is not greater than 2 (this cannot ever be the case), LF_ROWS is not lower than LF_BLKS (this can never be the case, the block will be detached from the index structure when it is entirely empty), and HEIGHT is not 4 (why 4, does that mean that 2, 3, 5,7, etc. are OK?).</p>
<p>Continuing the test, we will insert a single row into the table, and repopulate INDEX_STATS for the index:</p>
<pre>
INSERT INTO T1 VALUES (1,'A');
 
COMMIT;
 
ANALYZE INDEX IND_T1_C1 VALIDATE STRUCTURE;
 
SELECT
  LF_ROWS,
  DEL_LF_ROWS,
  HEIGHT,
  LF_BLKS,
  ROUND(DEL_LF_ROWS/LF_ROWS*100,2) PERCENT
FROM
  INDEX_STATS;
 
LF_ROWS DEL_LF_ROWS     HEIGHT    LF_BLKS    PERCENT
------- ----------- ---------- ---------- ----------
   9516        9515          2         21      99.99
</pre>
<p>As shown above, inserting a single row into the table cleared out 485 deleted rows (plus 1 for the inserted row) from the index leafs.  Based on what the book states, we do not need to rebuild this index.  DEL_LF_ROWS/LF_ROWS = 0.9999, which is not greater than 2.</p>
<p>Continuing the test, inserting a row with a value that is 1 greater than the previously deleted maximum value:</p>
<pre>
INSERT INTO T1 VALUES (10001,'A');
 
COMMIT;
 
ANALYZE INDEX IND_T1_C1 VALIDATE STRUCTURE;
 
SELECT
  LF_ROWS,
  DEL_LF_ROWS,
  HEIGHT,
  LF_BLKS,
  ROUND(DEL_LF_ROWS/LF_ROWS*100,2) PERCENT
FROM
  INDEX_STATS;
 
LF_ROWS DEL_LF_ROWS     HEIGHT    LF_BLKS    PERCENT
------- ----------- ---------- ---------- ----------
   9103        9101          2         21      99.98
</pre>
<p>As shown above, inserting a single row into the table cleared out an additional 414 deleted rows (plus 1 for the inserted row) from the index leafs.  Based on what the book states, we do not need to rebuild this index.  DEL_LF_ROWS/LF_ROWS = 0.9998, which is not greater than 2.</p>
<p>Continuing the test, inserting 100 rows into the table, with the values spaced 100 apart (100, 200, 300, etc.):</p>
<pre>
INSERT INTO
  T1 
SELECT
  ROWNUM*100 C1,
  RPAD('A',200,'A') C2
FROM
  DUAL
CONNECT BY
  LEVEL&lt;=100;
 
COMMIT;
 
ANALYZE INDEX IND_T1_C1 VALIDATE STRUCTURE;
 
SELECT
  LF_ROWS,
  DEL_LF_ROWS,
  HEIGHT,
  LF_BLKS,
  ROUND(DEL_LF_ROWS/LF_ROWS*100,2) PERCENT
FROM
  INDEX_STATS;
 
LF_ROWS DEL_LF_ROWS     HEIGHT    LF_BLKS    PERCENT
------- ----------- ---------- ---------- ----------
    102           0          2         21          0
</pre>
<p>As shown above, inserting 100 rows into the table cleared out 9101 additional deleted rows (plus 100 for the inserted rowed) from the index leafs.  Based on what the book states, we do not need to rebuild this index.  DEL_LF_ROWS/LF_ROWS = 0.0000, which is not greater than 2.</p>
<p>This index is now sparsely populated with on average less than 5 rows per leaf block, when it started out with an average of 476.19 rows per leaf block.  Should this index be rebuilt?  I suppose that depends on how the index will be used in the future, maybe a coalesce is a better choice, maybe doing nothing is a better choice.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
