jump to navigation

Oracle 19c Automatic Indexing: Mixing Manual and Automatic Indexes Part I (I Can’t Read) April 21, 2020

Posted by Richard Foote in 19c, 19c New Features, Automatic Indexing, CBO, Clustering Factor, Mixing Auto and Manual Indexes, Oracle Indexes.
trackback

tin machine album

In previous articles, I discussed how Automatic Indexing has the capability to add columns or reorder the column list of previously created Automatic Indexes. However, how does Automatic Indexing handle these types of scenarios with regard to existing manually created indexes?

To investigate, let’s create a table identical to the table I created in my previous blog post where Automatic Indexing created an index that was ultimately not used by the CBO because although Automatic Indexing finds the new index more efficient, the CBO costs it as being too expensive and ignores it.

SQL> create table major_tom5 (id number, code1 number, code2 number, code3 number, name varchar2(42));

Table created.

SQL> insert into major_tom5 select rownum, mod(rownum, 1000)+1, ceil(dbms_random.value(0, 100)), ceil(dbms_random.value(0, 10)),  'David Bowie' from dual connect by level = 10000000;

10000000 rows created.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats(ownname=>null, tabname=>'MAJOR_TOM5');

PL/SQL procedure successfully completed.

However, in this demo, I’m going to first create a manual index, but with the column list in CODE3, CODE2 order. This is the opposite order in which a default Automatic Index would be created (CODE2, CODE3 order) as this is the order of the columns in the table definition:

SQL> create index major_tom5_code3_code2_i on major_tom5(code3, code2);

Index created.

SQL&gt; select index_name, auto, constraint_index, visibility, compression, status, num_rows, leaf_blocks, clustering_factor <span style="color:var(--color-text);">from user_indexes where table_name='MAJOR_TOM5';</span>

INDEX_NAME                AUT CON VISIBILIT COMPRESSION   STATUS   NUM_ROWS   LEAF_BLOCKS CLUSTERING_FACTOR
------------------------- --- --- --------- ------------- -------- ---------- ----------- -----------------
MAJOR_TOM5_CODE3_CODE2_I  NO  NO  VISIBLE   DISABLED      VALID      10000000       24181           8974538

The resultant index has a terrible Clustering Factor of 8974538 on a 10M row table.

If we run the following query with filtering predicates on these 2 indexed columns:

SQL> select * from major_tom5 where code3=4 and code2=42;

10051 rows selected.

Execution Plan
-------------------------------------------------------------------------------------------
| Id  | Operation                    | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |            |  9982 |   272K|  7355   (7)| 00:00:01 |
|   1 |  PX COORDINATOR              |            |       |       |            |          |
|   2 |   PX SEND QC (RANDOM)        | :TQ10000   |  9982 |   272K|  7355   (7)| 00:00:01 |
|   3 |    PX BLOCK ITERATOR         |            |  9982 |   272K|  7355   (7)| 00:00:01 |
|*  4 |     TABLE ACCESS STORAGE FULL| MAJOR_TOM5 |  9982 |   272K|  7355   (7)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   4 - storage("CODE2"=42 AND "CODE3"=4)
       filter("CODE2"=42 AND "CODE3"=4)

Statistics
----------------------------------------------------------
         6  recursive calls
         0  db block gets
     45888  consistent gets
        68  physical reads
      5256  redo size
    149822  bytes sent via SQL*Net to client
       610  bytes received via SQL*Net from client
         4  SQL*Net roundtrips to/from client
         0  sorts (memory)
         0  sorts (disk)
     10051  rows processed

The CBO decides to NOT use the available index as it deems it too expensive, especially with such a poor Clustering Factor, to return the resultant 10,051 rows.

But what will Automatic Indexing do now. If we wait the 15 minute period until the next Automatic Indexing period and look at the resultant Automatic Indexing report:

INDEX DETAILS

-------------------------------------------------------------------------------
The following indexes were created:
*: invisible
---------------------------------------------------------------------------------
| Owner | Table      | Index                | Key         | Type   | Properties |
---------------------------------------------------------------------------------
| BOWIE | MAJOR_TOM5 | SYS_AI_2ajmncxsmg189 | CODE2,CODE3 | B-TREE | NONE       |
---------------------------------------------------------------------------------

VERIFICATION DETAILS
-------------------------------------------------------------------------------
The performance of the following statements improved:
-------------------------------------------------------------------------------

Parsing Schema Name  : BOWIE
SQL ID               : fmpwux2ptvasq
SQL Text             : select * from major_tom5 where code2=42 and code3=4
Improvement Factor   : 5.1x

Automatic Indexing has created a new index based on the column list CODE2, CODE3, because it considers such an index would improve performance of the query by a factor of 5.1x.

However, it has not recognised that the existing manual index based the column list CODE3, CODE2 would have done precisely the same job.

If we look further on in the Automatic Indexing report:

Execution Statistics:
-----------------------------
                              Original Plan                 Auto Index Plan
                              ----------------------------  ----------------------------
Elapsed Time (s):             993225                        26436
CPU Time (s):                 963727                        22535
Buffer Gets:                  137756                        9000
Optimizer Cost:               7355                          9069
Disk Reads:                   0                             26
Direct Writes:                0                             0
Rows Processed:               30153                         10051
Executions:                   3                             1

PLANS SECTION
---------------------------------------------------------------------------------------------
- Original
-----------------------------

Plan Hash Value  : 2129981950
---------------------------------------------------------------------------------------
| Id | Operation                      | Name       | Rows   | Bytes  | Cost  | Time    |
---------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT               |            |        |        |  7355 |         |
|  1 |  PX COORDINATOR                |            |        |        |       |         |
|  2 |    PX SEND QC (RANDOM)         | :TQ10000   |  10000 | 280000 |  7355 | 00:00:01|
|  3 |     PX BLOCK ITERATOR          |            |  10000 | 280000 |  7355 | 00:00:01|
|  4 |      TABLE ACCESS STORAGE FULL | MAJOR_TOM5 |  10000 | 280000 |  7355 | 00:00:01|
---------------------------------------------------------------------------------------

- With Auto Indexes
-----------------------------

Plan Hash Value  : 459198994
---------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name                 | Rows  | Bytes  | Cost | Time     |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                      | 10159 | 284452 | 9069 | 00:00:01 |
|   1 |   TABLE ACCESS BY INDEX ROWID BATCHED | MAJOR_TOM5           | 10159 | 284452 | 9069 | 00:00:01 |
| * 2 |    INDEX RANGE SCAN                   | SYS_AI_2ajmncxsmg189 | 10051 |        |   27 | 00:00:01 |
---------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("CODE2"=42 AND "CODE3"=4)

We notice the new execution plan using the newly created Automatic Index actually has a greater CBO cost than the previous FTS execution plan.

As we discussed in the previous post on when Automatic Indexing creating indexes that are not ultimately used by the CBO, although Automatic Indexing has indeed created this index because it has determined it’s going to be more efficient by a factor of 5.1x due to the reduction in Buffer Gets (137756 buffer gets old plan / 3 executions = 45,919 / 9000 buffer gets with index = 5.1), the CBO considers the execution plan using the Automatic Index to have a larger cost at 9069 than the previous FTS cost at just 7355.

Again just as with the existing, logically equivalent manually created index, the reason why the new Automatic Index is deemed too expensive by the CBO is because it likewise has the same terrible Clustering Factor:

SQL> select index_name, auto, constraint_index, visibility, compression, status, num_rows, leaf_blocks, clustering_factor from user_indexes where table_name='MAJOR_TOM5';

INDEX_NAME               AUT CON VISIBILIT COMPRESSION   STATUS     NUM_ROWS LEAF_BLOCKS CLUSTERING_FACTOR
------------------------ --- --- --------- ------------- -------- ---------- ----------- -----------------
MAJOR_TOM5_CODE3_CODE2_I NO  NO  VISIBLE   DISABLED      VALID      10000000       24181           8974538
SYS_AI_2ajmncxsmg189     YES NO  VISIBLE   DISABLED      VALID      10000000       23697           8974538

If we re-run the initial query again with the newly created Visible/Valid Automatic Index:

SQL> select * from major_tom5 where code3=4 and code2=42;

10051 rows selected.

Execution Plan
-------------------------------------------------------------------------------------------
| Id  | Operation                    | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |            |  9982 |   272K|  7355   (7)| 00:00:01 |
|   1 |  PX COORDINATOR              |            |       |       |            |          |
|   2 |   PX SEND QC (RANDOM)        | :TQ10000   |  9982 |   272K|  7355   (7)| 00:00:01 |
|   3 |    PX BLOCK ITERATOR         |            |  9982 |   272K|  7355   (7)| 00:00:01 |
|*  4 |     TABLE ACCESS STORAGE FULL| MAJOR_TOM5 |  9982 |   272K|  7355   (7)| 00:00:01 |
-------------------------------------------------------------------------------------------

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

    4 - storage("CODE2"=42 AND "CODE3"=4)
        filter("CODE2"=42 AND "CODE3"=4)

Statistics
----------------------------------------------------------
         6  recursive calls
         0  db block gets
     45888  consistent gets
        68  physical reads
      5256  redo size
    149822  bytes sent via SQL*Net to client
       610  bytes received via SQL*Net from client
         4  SQL*Net roundtrips to/from client
         0  sorts (memory)
         0  sorts (disk)
     10051  rows processed

The CBO ignores the newly created Automatic Index as it did the logically equivalent manually created index and uses the previous, cheaper FTS execution plan.

Automatic Indexing was NOT able to recognise that we already had an equivalent manually created index and so now we have TWO indexes that the CBO simply ignores as being too expensive…

More on mixing Automatic and Manual Indexes on my next post.

Comments»

1. Oracle 19c Automatic Indexing: Mixing Manual and Automatic Indexes Part II (Stay) | Richard Foote's Oracle Blog - May 6, 2020

[…] my previous post, I discussed how Automatic Indexing did not recognise there was already an existing logically […]

Like

2. Rajeshwaran Jeyabal - March 7, 2022

Richard,

thanks for the nice demo, was trying that in our Exacc platform running on 19.13 version.
could see that AI can now recognize the manual indexes now.

here is my test results, to confirm that.

ai_demo@PDB19> create table DEMO009b
2 nologging as
3 select rownum as id,
4 mod(rownum,1000)+1 as x1,
5 ceil(dbms_random.value(0,100)) as x2,
6 ceil(dbms_random.value(0,10)) as x3,
7 a.object_name
8 from all_objects a,
9 all_users, all_users
10 where rownum
ai_demo@PDB19> begin
2 dbms_output.put_line(
3 dbms_stats.create_extended_stats(user,’DEMO009b’,'(X2,X3)’)
4 );
5 end;
6 /
SYS_STU$LBMUZV7AA4J6YKVU6ZFPZC

PL/SQL procedure successfully completed.

ai_demo@PDB19>
ai_demo@PDB19> begin
2 dbms_stats.gather_table_stats(user,’DEMO009b’,
3 no_invalidate=>false,
4 method_opt=>’for all columns size auto for all hidden columns size 2048′,
5 degree=>8 );
6 end;
7 /

PL/SQL procedure successfully completed.

ai_demo@PDB19>
ai_demo@PDB19> select column_name,num_distinct,num_nulls,density
2 from user_tab_cols
3 where table_name =’DEMO009B’
4 and ( column_name like ‘X%’
5 or column_name like ‘ID’ )
6 order by column_name;

COLUMN_NAM NUM_DISTINCT NUM_NULLS DENSITY
———- ———— ———- ———-
ID 9914368 0 1.0086E-07
X1 1000 0 .001
X2 100 0 .01
X3 10 0 .1

ai_demo@PDB19>
ai_demo@PDB19> create index demo009b_idx1
2 on demo009b( x3,x2 )
3 nologging ;

Index created.

ai_demo@PDB19>
ai_demo@PDB19> select index_name,auto,tablespace_name,compression,
2 status,visibility,indexing
3 from user_indexes
4 where table_name =’DEMO009B’;

INDEX_NAME AUT TABLESPACE_NAME COMPRESSION STATUS VISIBILIT INDEXIN
————————- — ————————- ————- ——– ——— ——-
DEMO009B_IDX1 NO TS_INDEX_DEMO DISABLED VALID VISIBLE FULL

ai_demo@PDB19>
ai_demo@PDB19> select index_name,column_name,column_position
2 from user_ind_columns
3 where table_name =’DEMO009B’
4 order by 1,3;

INDEX_NAME COLUMN_NAM COLUMN_POSITION
————————- ———- —————
DEMO009B_IDX1 X3 1
DEMO009B_IDX1 X2 2

ai_demo@PDB19>
ai_demo@PDB19> col x2 new_val x2
ai_demo@PDB19> col x3 new_val x3
ai_demo@PDB19> select x2,x3,count(*)
2 from demo009b
3 group by x2,x3
4 having count(*) > 1
5 order by 3 desc
6 fetch first 1 row only;

X2 X3 COUNT(*)
———- ———- ———-
20 3 10301

ai_demo@PDB19>
ai_demo@PDB19> select max(x1), count(*) from demo009b where x2 = &x2 and x3 = &x3;
old 1: select max(x1), count(*) from demo009b where x2 = &x2 and x3 = &x3
new 1: select max(x1), count(*) from demo009b where x2 = 20 and x3 = 3

MAX(X1) COUNT(*)
———- ———-
1000 10301

ai_demo@PDB19> select max(x1), count(*) from demo009b where x2 = &x2 and x3 = &x3;
old 1: select max(x1), count(*) from demo009b where x2 = &x2 and x3 = &x3
new 1: select max(x1), count(*) from demo009b where x2 = 20 and x3 = 3

MAX(X1) COUNT(*)
———- ———-
1000 10301

ai_demo@PDB19> select max(x1), count(*) from demo009b where x2 = &x2 and x3 = &x3;
old 1: select max(x1), count(*) from demo009b where x2 = &x2 and x3 = &x3
new 1: select max(x1), count(*) from demo009b where x2 = 20 and x3 = 3

MAX(X1) COUNT(*)
———- ———-
1000 10301

ai_demo@PDB19>
ai_demo@PDB19> host timeout /T 900

ai_demo@PDB19> select dbms_auto_index.report_activity( activity_start=> systimestamp – 2/24 ) report from dual;

REPORT
————————————————————————————————————————————————————————————
GENERAL INFORMATION
——————————————————————————-
Activity start : 07-MAR-2022 03:08:02
Activity end : 07-MAR-2022 05:08:02
Executions completed : 8
Executions interrupted : 0
Executions with fatal error : 0
——————————————————————————-

SUMMARY (AUTO INDEXES)
——————————————————————————-
Index candidates : 0
Indexes created : 0
Space used : 0 B
Indexes dropped : 0
SQL statements verified : 0
SQL statements improved : 0
SQL plan baselines created : 0
Overall improvement factor : 0x
——————————————————————————-

SUMMARY (MANUAL INDEXES)
——————————————————————————-
Unused indexes : 0
Space used : 0 B
Unusable indexes : 0
——————————————————————————-

ERRORS
———————————————————————————————
No errors found.
———————————————————————————————

ai_demo@PDB19>
ai_demo@PDB19> select owner,index_name,tablespace_name,compression,
2 status,visibility,indexing,auto
3 from all_indexes
4 where table_name =’DEMO009B’
5 and owner =’AI_DEMO’;

OWNER INDEX_NAME TABLESPACE_NAME COMPRESSION STATUS VISIBILIT INDEXIN AUT
———- ————————- ————————- ————- ——– ——— ——- —
AI_DEMO DEMO009B_IDX1 TS_INDEX_DEMO DISABLED VALID VISIBLE FULL NO

ai_demo@PDB19>
ai_demo@PDB19> select index_name,column_name,column_position
2 from user_ind_columns
3 where table_name =’DEMO009B’
4 order by 1,3;

INDEX_NAME COLUMN_NAM COLUMN_POSITION
————————- ———- —————
DEMO009B_IDX1 X3 1
DEMO009B_IDX1 X2 2

ai_demo@PDB19>
ai_demo@PDB19> host timeout /T 900

ai_demo@PDB19> select dbms_auto_index.report_activity( activity_start=> systimestamp – 2/24 ) report from dual;

REPORT
————————————————————————————————————————————————————————————
GENERAL INFORMATION
——————————————————————————-
Activity start : 07-MAR-2022 03:23:04
Activity end : 07-MAR-2022 05:23:04
Executions completed : 8
Executions interrupted : 0
Executions with fatal error : 0
——————————————————————————-

SUMMARY (AUTO INDEXES)
——————————————————————————-
Index candidates : 1
Indexes created (visible / invisible) : 1 (0 / 1)
Space used (visible / invisible) : 134.22 MB (0 B / 134.22 MB)
Indexes dropped : 0
SQL statements verified : 1
SQL statements improved : 0
SQL plan baselines created : 0
Overall improvement factor : 0x
——————————————————————————-

SUMMARY (MANUAL INDEXES)
——————————————————————————-
Unused indexes : 0
Space used : 0 B
Unusable indexes : 0
——————————————————————————-

INDEX DETAILS
——————————————————————————-
1. The following indexes were created:
——————————————————————————-
—————————————————————————
| Owner | Table | Index | Key | Type | Properties |
—————————————————————————
| AI_DEMO | DEMO009B | SYS_AI_7mx8mdmfk2saq | X2,X3 | B-TREE | NONE |
—————————————————————————
——————————————————————————-

ERRORS
———————————————————————————————
No errors found.
———————————————————————————————

ai_demo@PDB19>
ai_demo@PDB19> select owner,index_name,tablespace_name,compression,
2 status,visibility,indexing,auto
3 from all_indexes
4 where table_name =’DEMO009B’
5 and owner =’AI_DEMO’;

OWNER INDEX_NAME TABLESPACE_NAME COMPRESSION STATUS VISIBILIT INDEXIN AUT
———- ————————- ————————- ————- ——– ——— ——- —
AI_DEMO DEMO009B_IDX1 TS_INDEX_DEMO DISABLED VALID VISIBLE FULL NO
AI_DEMO SYS_AI_7mx8mdmfk2saq TS_INDEX_DEMO ADVANCED LOW VALID INVISIBLE FULL YES

ai_demo@PDB19>
ai_demo@PDB19> select index_name,column_name,column_position
2 from user_ind_columns
3 where table_name =’DEMO009B’
4 order by 1,3;

INDEX_NAME COLUMN_NAM COLUMN_POSITION
————————- ———- —————
DEMO009B_IDX1 X3 1
DEMO009B_IDX1 X2 2
SYS_AI_7mx8mdmfk2saq X2 1
SYS_AI_7mx8mdmfk2saq X3 2

ai_demo@PDB19>

Like

Richard Foote - May 2, 2022

Hi Rajeshwaran

Thanks for your demo. I’ll check it out and get back with some comments.

Regards

Richard

Like

Richard Foote - May 2, 2022

Hi Rajeshwaran

Not sure how your demo shows that things have changed (other than now having a Valid/Invisible index as I explained previously):

Automatic Indexing: 3 Possible States Of Newly Created Automatic Indexes Part II (“Because You’re Young”)

Oracle is still (unnecessarily) creating an index on X2,X3, when it serves no better purpose than the manual X3,X2

Regards

Richard

Like


Leave a comment