Re: Composite index question

From: <nickli2000_at_gmail.com>
Date: Sun, 13 Jan 2008 21:38:08 -0800 (PST)
Message-ID: <2bd7927c-56b7-4a02-8459-63313ebaff70@k39g2000hsf.googlegroups.com>


On Jan 13, 10:19�pm, Charles Hooper <hooperc2..._at_yahoo.com> wrote:
> On Jan 13, 6:32�pm, Charles Hooper <hooperc2..._at_yahoo.com> wrote:
>
>
>
> > On Jan 13, 3:34�pm, nickli2..._at_gmail.com wrote:
>
> > > Hi,
>
> > > � I have a question on how composite index should be used. As an
> > > example, my queries often select two leading columns, column_1 and
> > > column_2 of a table. Should I create a composite index using column_1
> > > and column_2, �or should I create two separated indexes for column_1
> > > and column_2? What are the differences between these two approaches
> > > and performance implications?
>
> > > � Thanks in advance.
>
> > > � Nick
>
> > I don't think that you have provided enough information for a
> > reasonably accurate answer to be provided. �There are too many
> > possible exceptions to any type of broad indexing rule for this
> > situation.
>
> Continuing my previous post.. this time with a table with much wider
> columns, and less randomness from one row to the next for the first
> two columns, which will be indexed. �The results are a bit different
> from the previous results, and in some cases Oracle selected to use an
> index on a single column when both columns of the composite index were
> specified in the WHERE clause.
>
> CREATE TABLE T2(
> � COLUMN_1 VARCHAR2(40) NOT NULL,
> � COLUMN_2 VARCHAR2(40) NOT NULL,
> � COLUMN_3 VARCHAR2(255),
> � COLUMN_4 VARCHAR2(255),
> � COLUMN_5 VARCHAR2(255),
> � COLUMN_6 VARCHAR2(255),
> � COLUMN_7 VARCHAR2(255),
> � COLUMN_8 VARCHAR2(255),
> � COLUMN_9 VARCHAR2(255),
> � COLUMN_10 VARCHAR2(255));
>
> INSERT INTO
> � T2
> SELECT
> � TRIM(TO_CHAR(ROWNUM,'000000'))||DBMS_RANDOM.STRING('A',10),
> � TRIM(TO_CHAR(ROWNUM*3.14,'000000'))||DBMS_RANDOM.STRING('A',34),
> � DBMS_RANDOM.STRING('A',255),
> � DBMS_RANDOM.STRING('A',255),
> � DBMS_RANDOM.STRING('A',255),
> � DBMS_RANDOM.STRING('A',255),
> � DBMS_RANDOM.STRING('A',255),
> � DBMS_RANDOM.STRING('A',255),
> � DBMS_RANDOM.STRING('A',255),
> � DBMS_RANDOM.STRING('A',255)
> FROM
> � {a table with at least 100,000 rows}
> WHERE
> � ROWNUM<=100000;
>
> Each row will contain roughly 2096 bytes, and if the blocks are 80%
> used, that is roughly 262MB worth of data.
>
> COMMIT;
>
> CREATE UNIQUE INDEX T2_IND1 ON T2(COLUMN_1,COLUMN_2);
> CREATE INDEX T2_IND2 ON T2(COLUMN_1);
> CREATE INDEX T2_IND3 ON T2(COLUMN_2);
>
> EXEC
> DBMS_STATS.GATHER_TABLE_STATS(OWNNAME=>USER,TABNAME=>'T2',CASCADE=>TRUE);
>
> SELECT
> � COLUMN_1,
> � COLUMN_2
> FROM
> � T2;
>
> ---------------------------------------------------------------------------�---------------
> | Id �| Operation � � � � � �| Name � �| Starts | E-Rows | A-Rows |
> A-Time � | Buffers |
> ---------------------------------------------------------------------------�---------------
> | � 1 | �INDEX FAST FULL SCAN| T2_IND1 | � � �1 | � �100K| � �100K|
> 00:00:00.20 | � �1916 |
> ---------------------------------------------------------------------------�---------------
>
> --
> SELECT
> � COLUMN_1,
> � COLUMN_2,
> � COLUMN_3
> FROM
> � T2
> WHERE
> � COLUMN_1<='001800';
>
> ---------------------------------------------------------------------------�-------------------------------
> | Id �| Operation � � � � � � � � � | Name � �| Starts | E-Rows | A-
> Rows | � A-Time � | Buffers | Reads �|
> ---------------------------------------------------------------------------�-------------------------------
> | � 1 | �TABLE ACCESS BY INDEX ROWID| T2 � � �| � � �1 | � � 46 |
> 1799 |00:00:00.42 | � � 638 | � �562 |
> |* �2 | � INDEX RANGE SCAN � � � � �| T2_IND2 | � � �1 | � � 46 |
> 1799 |00:00:00.01 | � � �26 | � � �0 |
> ---------------------------------------------------------------------------�-------------------------------
>
> Predicate Information (identified by operation id):
> ---------------------------------------------------
> � �2 - access("COLUMN_1"<='001800')
>
> --
> SELECT
> � COLUMN_1,
> � COLUMN_2,
> � COLUMN_3
> FROM
> � T2
> WHERE
> � COLUMN_1<'001800'
> � AND COLUMN_2<'005652';
>
> ---------------------------------------------------------------------------�----------------------
> | Id �| Operation � � � � � � � � � | Name � �| Starts | E-Rows | A-
> Rows | � A-Time � | Buffers |
> ---------------------------------------------------------------------------�----------------------
> |* �1 | �TABLE ACCESS BY INDEX ROWID| T2 � � �| � � �1 | � � �1 |
> 1799 |00:00:00.02 | � � 645 |
> |* �2 | � INDEX RANGE SCAN � � � � �| T2_IND3 | � � �1 | � � �3 |
> 1799 |00:00:00.01 | � � �33 |
> ---------------------------------------------------------------------------�----------------------
>
> Predicate Information (identified by operation id):
> ---------------------------------------------------
> � �1 - filter("COLUMN_1"<'001800')
> � �2 - access("COLUMN_2"<'005652')
>
> Note that the above example did not use the composite index, even
> though both columns in the composite index were included in the WHERE
> clause.
>
> --
> Instruct Oracle to use the composite index:
> SELECT /*+ INDEX(T2,T2_IND1) */
> � COLUMN_1,
> � COLUMN_2,
> � COLUMN_3
> FROM
> � T2
> WHERE
> � COLUMN_1<'001800'
> � AND COLUMN_2<'005652';
>
> ---------------------------------------------------------------------------�----------------------
> | Id �| Operation � � � � � � � � � | Name � �| Starts | E-Rows | A-
> Rows | � A-Time � | Buffers |
> ---------------------------------------------------------------------------�----------------------
> | � 1 | �TABLE ACCESS BY INDEX ROWID| T2 � � �| � � �1 | � � �1 |
> 1799 |00:00:00.02 | � � 649 |
> |* �2 | � INDEX RANGE SCAN � � � � �| T2_IND1 | � � �1 | � � �1 |
> 1799 |00:00:00.01 | � � �37 |
> ---------------------------------------------------------------------------�----------------------
>
> Predicate Information (identified by operation id):
> ---------------------------------------------------
> � �2 - access("COLUMN_1"<'001800' AND "COLUMN_2"<'005652')
> � � � �filter("COLUMN_2"<'005652')
>
> --
> An index being used to satisfy a query when grouping on the SUBSTR of
> COLUMN_2:
> SELECT
> � SUBSTR(COLUMN_2,7,5),
> � COUNT(*)
> FROM
> � T2
> GROUP BY
> � SUBSTR(COLUMN_2,7,5)
> HAVING
> � COUNT(*)>1;
>
> ---------------------------------------------------------------------------�-----------------------------------
> | Id �| Operation � � � � � � �| Name � �| Starts | E-Rows | A-Rows
> | � A-Time � | Buffers | Reads �| Writes |
> ---------------------------------------------------------------------------�-----------------------------------
> |* �1 | �FILTER � � � � � � � �| � � � � | � � �1 | � � � �| � � 15 |
> 00:00:01.11 | � � 736 | � �187 | � �186 |
> | � 2 | � HASH GROUP BY � � � �| � � � � | � � �1 | � 5000 | �99985 |
> 00:00:00.95 | � � 736 | � �187 | � �186 |
> | � 3 | � �INDEX FAST FULL SCAN| T2_IND3 | � � �1 | � �100K| � �100K|
> 00:00:00.25 | � � 736 | � � �1 | � � �0 |
> ---------------------------------------------------------------------------�-----------------------------------
>
> Predicate Information (identified by operation id):
> ---------------------------------------------------
> � �1 - filter(COUNT(*)>1)
>
> --
> Let's force it to use the composite index:
> SELECT /*+ INDEX(T2,T2_IND1) */
> � SUBSTR(COLUMN_2,7,5),
> � COUNT(*)
> FROM
> � T2
> GROUP BY
> � SUBSTR(COLUMN_2,7,5)
> HAVING
> � COUNT(*)>1;
>
> ---------------------------------------------------------------------------�------------------------------
> | Id �| Operation � � � � | Name � �| Starts | E-Rows | A-Rows | � A-
> Time � | Buffers | Reads �| Writes |
> ---------------------------------------------------------------------------�------------------------------
> |* �1 | �FILTER � � � � � | � � � � | � � �1 | � � � �| � � 15 |
> 00:00:01.05 | � � 955 | � �186 | � �186 |
> | � 2 | � HASH GROUP BY � | � � � � | � � �1 | � 5000 | �99985 |
> 00:00:00.91 | � � 955 | � �186 | � �186 |
> | � 3 | � �INDEX FULL SCAN| T2_IND1 | � � �1 | � �100K| � �100K|
> 00:00:00.20 | � � 955 | � � �0 | � � �0 |
> ---------------------------------------------------------------------------�------------------------------
>
> Predicate Information (identified by operation id):
> ---------------------------------------------------
> � �1 - filter(COUNT(*)>1)
>
> The above seems to indicate that the composite index allowed the query
> to complete slighly faster.
>
> --
> SELECT
> � T2.COLUMN_1,
> � T2.COLUMN_2,
> � T2.COLUMN_3,
> � T2.COLUMN_4
> FROM
> � (SELECT
> � � COLUMN_1,
> � � COLUMN_2,
> � � COLUMN_3
> � FROM
> � � T2
> � WHERE
> � � ROWNUM<=100) V_T2,
> � T2
> WHERE
> � V_T2.COLUMN_1=T2.COLUMN_1
> � AND V_T2.COLUMN_2=T2.COLUMN_2;
>
> ---------------------------------------------------------------------------�----------------------
> | Id �| Operation � � � � � � � � � | Name � �| Starts | E-Rows | A-
> Rows | � A-Time � | Buffers |
> ---------------------------------------------------------------------------�----------------------
> |* �1 | �TABLE ACCESS BY INDEX ROWID| T2 � � �| � � �1 | � � �1 |
> 100 |00:00:00.01 | � � 145 |
> | � 2 | � NESTED LOOPS � � � � � � �| � � � � | � � �1 | � �100 |
> 201 |00:00:00.01 | � � 111 |
> | � 3 | � �VIEW � � � � � � � � � � | � � � � | � � �1 | � �100 |
> 100 |00:00:00.01 | � � � 8 |
> |* �4 | � � COUNT STOPKEY � � � � � | � � � � | � � �1 | � � � �|
> 100 |00:00:00.01 | � � � 8 |
> | � 5 | � � �INDEX FAST FULL SCAN � | T2_IND1 | � � �1 | � �100K|
> 100 |00:00:00.01 | � � � 8 |
> |* �6 | � �INDEX RANGE SCAN � � � � | T2_IND2 | � �100 | � � �1 |
> 100 |00:00:00.01 | � � 103 |
> ---------------------------------------------------------------------------�----------------------
>
> Predicate Information (identified by operation id):
> ---------------------------------------------------
> � �1 - filter("V_T2"."COLUMN_2"="T2"."COLUMN_2")
> � �4 - filter(ROWNUM<=100)
> � �6 - access("V_T2"."COLUMN_1"="T2"."COLUMN_1")
>
> Note that in the above, Oracle uses the composite index on
> COLUMN_1,COLUMN_2 for the inline view, but only T2_IND2 (on COLUMN_1)
> for the join between the inline view and the table T2, even though
> COLUMN_1 and COLUMN_2 where specified in the WHERE clause.
>
> --
> SELECT
> � T2.COLUMN_1,
> � T2.COLUMN_2,
> � T2.COLUMN_3,
> � T2.COLUMN_4
> FROM
> � (SELECT
> � � COLUMN_1,
> � � COLUMN_2,
> � � COLUMN_3
> � FROM
> � � T2
> � WHERE
> � � ROWNUM<=100) V_T2,
> � T2
> WHERE
> � V_T2.COLUMN_1=T2.COLUMN_1
> � AND V_T2.COLUMN_2=T2.COLUMN_2
> � AND V_T2.COLUMN_3=T2.COLUMN_3;
>
> ---------------------------------------------------------------------------�----------------------
> | Id �| Operation � � � � � � � � � | Name � �| Starts | E-Rows | A-
> Rows | � A-Time
> ...
>
> read more �- Hide quoted text -
>
> - Show quoted text -

Charles,

   Thanks a lot for your help and it really helps. I will go back and hit some Oracle books.

   Nick Received on Sun Jan 13 2008 - 23:38:08 CST

Original text of this message