+ All Categories
Home > Documents > 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often...

10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often...

Date post: 11-Jun-2020
Category:
Upload: others
View: 2 times
Download: 0 times
Share this document with a friend
96
© RICHARD FOOTE CONSULTING 1 10 Things You May Not Know About Oracle Indexes But Probably Should Richard Foote Consulting
Transcript
Page 1: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 1

10 Things You May Not Know About Oracle Indexes But Probably Should

Richard Foote Consulting

Page 2: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 2

Or As Many Things You May Not Know About Oracle Indexes But Probably Should That I Can Fit in 45 Minutes

Richard Foote Consulting

Page 3: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 3

Richard Foote richardfoote

• Working in IT for 30+ years , 20+ years with Oracle Database

• 19 years employed in Australian Federal Government in various IT roles

• Worked for Oracle Corporation between 1996 and 2002 and between 2011 and 2017

• In September 2017, started my own independent company Richard Foote Consulting

• Been responsible for many large scale, mission critical, “life-dependant” classified Oracle systems, tuned numerous databases often with 10x performance improvements

• Oracle OakTable Member since 2002 and awarded Oracle ACE Director in 2008 & 2018

• Regular speaker at user group meetings and conferences such as Oracle OpenWorld, IOUG Collaborate, Hotsos Symposium, AUSOUG InSync, ODTUG Kscope, UKOUG Tech Conference, E4 Enkitec Extreme Exadata Expo, Trivadis Performance Days …

• UKOUG Lifetime Speaker award 2018 and AUSOUG Oracle Master award 2018

• Richard Foote's Oracle Blog: https://richardfoote.wordpress.com

• Richard Foote Consulting: https//richardfooteconsulting.com

• Spend as much free time as possible listening to the music of David Bowie !!

Page 4: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 4

“Oracle Indexing Internals & Best Practices” 2 Day Seminar

LONDON: 23-24 March 2020Of benefit to DBAs, Developers, Solution Architects and anyone else interested in designing, developing or maintaining high performance Oracle-based applications/databases.

• Examines most available index structures/options & discusses in considerable detail how indexes function, how/when they should be used & how they should be maintained.

• Details how indexes are costed & evaluated by the Cost Based Optimizer (CBO) & how appropriate data management practices are vital for an effective indexing strategy.

• Covers many useful tips and strategies to maximise the benefits of indexes on application/database performance & scalability.

richardfooteconsulting.com/indexing-webinar/

Page 5: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 5

“Oracle Performance Diagnostics and Tuning” 2 Day Seminar

LONDON: 25-26 March 2020

• The focus is at both the Database and individual SQL layers.• Bring your own AWR reports and have them reviewed following the Tuning

Methodology covered in the seminar to help find and resolve performance issues with your databases/applications !!

richardfooteconsulting.com/performance-tuning-seminar/

• A must attend seminar aimed at Oracle professionals (both DBAs and Developers) who are interested in Oracle Performance Tuning.

• Details how to maximise the performance of both Oracle databases and associated applications and how to diagnose and address any performance issues as quickly and effectively as possible.

Page 6: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 6

1. How CBO Determines Index Costings

Setup a simple demo table and indexes:

SQL> create table bowie (id number, code number, name varchar2(42));

Table created.

SQL> insert into bowie select rownum, mod(rownum,100)+1, 'ZIGGY STARDUST' from dual connect by level <= 100000;

100000 rows created.

SQL> commit;

SQL> create index bowie_code_i on bowie(code);

Index created.

SQL> create unique index bowie_id_i on bowie(id);

Index created.

SQL> exec dbms_stats.gather_table_stats(ownname=> null, tabname=> 'BOWIE', estimate_percent=> null, method_opt=> 'FOR ALL COLUMNS SIZE 1');

PL/SQL procedure successfully completed.

SQL> SELECT table_name, blocks, num_rows FROM user_tables WHERE table_name = 'BOWIE';

TABLE_NAME BLOCKS NUM_ROWS---------- ---------- ----------BOWIE 496 100000

Page 7: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 7

SQL> SELECT index_name, blevel, leaf_blocks, num_rows, distinct_keys, clustering_factorFROM user_indexes WHERE index_name = 'BOWIE_ID_I';

INDEX_NAME BLEVEL LEAF_BLOCKS NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR------------- ---------- ----------- ---------- ------------- -----------------BOWIE_ID_I 1 208 100000 100000 385

SQL> SELECT column_name, num_distinct, density, num_nullsFROM user_tab_col_statistics WHERE table_name = 'BOWIE' and column_name = 'ID';

COLUMN_NAME NUM_DISTINCT DENSITY NUM_NULLS------------ ------------ ---------- ----------ID 100000 .00001 0

SQL> SELECT column_name, endpoint_number, endpoint_value FROM user_histogramsWHERE table_name = 'BOWIE' and column_name = 'ID';

COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE------------ --------------- --------------ID 0 1ID 1 100000

1. How CBO Determines Index Costings

Page 8: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 8

SQL> SELECT index_name, blevel, leaf_blocks, num_rows, distinct_keys, clustering_factorFROM user_indexes WHERE index_name = 'BOWIE_CODE_I';

INDEX_NAME BLEVEL LEAF_BLOCKS NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR------------ ---------- ----------- ---------- ------------- -----------------BOWIE_CODE_I 1 195 100000 100 38500

SQL> SELECT column_name, num_distinct, density, num_nullsFROM user_tab_col_statistics WHERE table_name = 'BOWIE' and column_name = 'CODE';

COLUMN_NAME NUM_DISTINCT DENSITY NUM_NULLS------------ ------------ ---------- ----------CODE 100 .01 0

SQL> SELECT column_name, endpoint_number, endpoint_value FROM user_histogramsWHERE table_name = 'BOWIE' and column_name = 'CODE';

COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE------------ --------------- --------------CODE 0 1CODE 1 100

1. How CBO Determines Index Costings

Page 9: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 9

SQL> CREATE TABLE bowie2 AS SELECT * FROM bowie ORDER BY code;

Table created.

SQL> exec dbms_stats.gather_table_stats(ownname=> null, tabname=> 'BOWIE2', estimate_percent=> null, method_opt=> 'FOR ALL COLUMNS SIZE 1');

PL/SQL procedure successfully completed.

SQL> CREATE INDEX bowie2_code_i ON bowie2(code);

Index created.

SQL> SELECT index_name, blevel, leaf_blocks, num_rows, distinct_keys, clustering_factorFROM user_indexes WHERE index_name = 'BOWIE2_CODE_I';

INDEX_NAME BLEVEL LEAF_BLOCKS NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR------------- ---------- ----------- ---------- ------------- -----------------BOWIE2_CODE_I 1 195 100000 100 387

Create an almost identical table except it's ordered on the CODE column

Clustering Factor on CODE is now significantly reduced at 387 (down from 38500)

1. How CBO Determines Index Costings

Page 10: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 10

• optimizer_mode=ALL_ROWS

• optimzer_index_cost_adj=100

• optimizer_index_caching=0

• set arraysize 5000

SQL> SELECT pname, pval1 FROM SYS.AUX_STATS$WHERE pname IN ('SREADTIM', 'MREADTIM', 'MBRC', 'CPUSPEED');

PNAME PVAL1------------------------------ ----------SREADTIM 2MREADTIM 10CPUSPEED 3200MBRC 20

Cost Based Optimizer Related Settings

1. How CBO Determines Index Costings

Page 11: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 11

SQL> select * from bowie where id=42;

Execution Plan------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 23 | 2 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID| BOWIE | 1 | 23 | 2 (0)| 00:00:01 ||* 2 | INDEX UNIQUE SCAN | BOWIE_ID_I | 1 | | 1 (0)| 00:00:01 |------------------------------------------------------------------------------------------

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

2 - access("ID"=42)

Statistics----------------------------------------------------------

0 recursive calls0 db block gets3 consistent gets0 physical reads0 redo size

570 bytes sent via SQL*Net to client346 bytes received via SQL*Net from client1 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)1 rows processed

1. How CBO Determines Index Costings

Page 12: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 12

SQL> select * from bowie where code=42; <= This represents just 1% of the data

Execution Plan---------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1000 | 23000 | 129 (4)| 00:00:01 ||* 1 | TABLE ACCESS FULL| BOWIE | 1000 | 23000 | 129 (4)| 00:00:01 | <= Why the Full Table Scan ?---------------------------------------------------------------------------

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

1 - filter("CODE"=42)

Statistics----------------------------------------------------------

0 recursive calls0 db block gets

443 consistent gets0 physical reads0 redo size

10665 bytes sent via SQL*Net to client359 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)

1000 rows processed

1. How CBO Determines Index Costings

Page 13: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 13

SQL> select /*+ index(bowie) */ * from bowie where code=42;

Execution Plan----------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |----------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1000 | 23000 | 387 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| BOWIE | 1000 | 23000 | 387 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | BOWIE_CODE_I | 1000 | | 2 (0)| 00:00:01 |----------------------------------------------------------------------------------------------------

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

2 - access("CODE"=42)

Statistics----------------------------------------------------------

0 recursive calls0 db block gets

391 consistent gets0 physical reads0 redo size

28637 bytes sent via SQL*Net to client379 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)

1000 rows processed

1. How CBO Determines Index Costings

Page 14: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 14

Selectivity = Density of Code = 1/100 = 0.01Cardinality = No of rows x Density = 100000 x 0.01 = 1000I/O Index Cost = blevel - 1* +

ceil(effective index selectivity x leaf_blocks) +ceil(effective table selectivity x clustering_factor)

I/O Index Cost = 0 + ceil(0.01 x 195) + (0.01 x 38500)

I/O Index Cost = 0 + 2 + 385 = 387

Note: CPU % = 0 (too small to be considered)

* For Equality Predicates, if Blevel <=2, then Root Block is not counted

1. How CBO Determines Index Costings

Page 15: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 15

Cost (%CPU)129 (4)

FTS I/O Time = (table blocks / mbrc) x mreadtim

FTS I/O Time = (496 / 20) x 10 = 248ms

FTS IO Cost = I/O Time / sreadtim = 248ms / 2 = 124

FTS CPU Cost = 4% of 129 = 5

Total FTS Cost = FTS I/O Cost + FTS CPU Cost = 124 + 5 = 129

Note: 129 is less than the 387 when using the index, so CBO uses a FTS

1. How CBO Determines FTS Costings

Page 16: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 16

SQL> select * from bowie2 where code=42;

Execution Plan-----------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-----------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1000 | 23000 | 6 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| BOWIE2 | 1000 | 23000 | 6 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | BOWIE2_CODE_I | 1000 | | 2 (0)| 00:00:01 |-----------------------------------------------------------------------------------------------------

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

2 - access("CODE"=42)

Statistics----------------------------------------------------------

0 recursive calls0 db block gets11 consistent gets0 physical reads0 redo size

28637 bytes sent via SQL*Net to client360 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)

1000 rows processed

1. How CBO Determines Index Costings

Page 17: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 17

Index I/O Cost = blevel - 1 +ceil(effective index selectivity x leaf_blocks) +ceil(effective table selectivity x clustering_factor)

Index I/O Cost = 0 + ceil(0.01 x 195) + ceil(0.01 x 387)

Index I/O Cost = 0 + 2 + 4 = 6

CPU cost trivial at 0%

Note: the difference in the Clustering Factor value has made the huge difference to the comparable costings …

1. How CBO Determines Index Costings

Page 18: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 18

2. TABLE_CACHED_BLOCKS (12.1)

SQL> create table bowie_assm (id number, name varchar2(42));

Table created.

SQL> create sequence bowie_assm_seq order;

Sequence created.

SQL> create or replace procedure pop_bowie_assm as2 begin3 for i in 1..100000 loop4 insert into bowie_assm values (bowie_assm_seq.nextval, 'DAVID BOWIE');5 commit;6 end loop;7 end;8 /

Procedure created.

Create table in an ASSM Tablespace

In (say) 3 separate concurrent sessions, exec pop_bowie_assm

Page 19: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 19

SQL> create index bowie_assm_id_i on bowie_assm(id);

Index created.

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

PL/SQL procedure successfully completed.

SQL> SELECT t.table_name, i.index_name, t.blocks, t.num_rows, i.clustering_factorFROM user_tables t, user_indexes iWHERE t.table_name = i.table_name AND i.index_name='BOWIE_ASSM_ID_I';

TABLE_NAME INDEX_NAME BLOCKS NUM_ROWS CLUSTERING_FACTOR---------- ---------------------- ---------- ---------- -----------------BOWIE_ASSM BOWIE_ASSM_ID_I 1000 300000 217236

Data “inserted” in ID order. Index has extremely poor CF despite well clustered data

2. TABLE_CACHED_BLOCKS

Page 20: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 20

SQL> select * from bowie_assm where id between 42 and 429;

388 rows selected.

Execution Plan--------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |--------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 389 | 6613 | 275 (2)| 00:00:01 ||* 1 | TABLE ACCESS FULL| BOWIE_ASSM | 389 | 6613 | 275 (2)| 00:00:01 |--------------------------------------------------------------------------------

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

1 - filter("ID"<=429 AND "ID">=42)

Statistics----------------------------------------------------------

0 recursive calls0 db block gets

949 consistent gets0 physical reads0 redo size

4094 bytes sent via SQL*Net to client608 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)

388 rows processed

2. TABLE_CACHED_BLOCKS

Page 21: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 21

• Rebuilding index if CF is poor is common advice

• Unfortunately, as neither table nor index order changes, the net effect is “disappointing” (actually no effect whatsoever).

• To improve the CF, it's the table that must be rebuilt (and reordered)

• If table has multiple indexes, careful consideration needs to be given by which index to order table

• Pre-fetch index reads improves poor CF performance

• Rebuilding an index simply because it has a CF over a certain threshold is futile and a silly myth

2. TABLE_CACHED_BLOCKS

Page 22: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 22

SQL> alter index bowie_assm_id_i rebuild;

Index altered.

SQL> SELECT t.table_name, i.index_name, t.blocks, t.num_rows, i.clustering_factorFROM user_tables t, user_indexes iWHERE t.table_name = i.table_name AND i.index_name='BOWIE_ASSM_ID_I';

TABLE_NAME INDEX_NAME BLOCKS NUM_ROWS CLUSTERING_FACTOR---------- ---------------------- ---------- ---------- -----------------BOWIE_ASSM BOWIE_ASSM_ID_I 1000 300000 217236

The Clustering Factor remains identical…

2. TABLE_CACHED_BLOCKS

Page 23: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 23

• Bug 13262857 Enh: provide some control over DBMS_STATS index clustering factor computation INDEX

• Patch available for 11.1.0.7, 11.2.0.2, 11.2.0.3

• TABLE_CACHED_BLOCKS stats collection preference

• Can be set between 1 (default) and 255 to determine how many previous accessed blocks to ignore when incrementing CF

• Set preference via for example:

– DBMS_STATS.SET_TABLE_PREFS,

– DBMS_STATS.SET_SCHEMA_PREFS

– DBMS_STATS.SET_DATABASE_PREFS

2. TABLE_CACHED_BLOCKS

Page 24: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 24

SQL> exec dbms_stats.set_table_prefs(ownname=>user, tabname=>'BOWIE_ASSM', pname=>'TABLE_CACHED_BLOCKS', pvalue=>42);

PL/SQL procedure successfully completed.

SQL> exec dbms_stats.gather_index_stats(ownname=>user, indname=>'BOWIE_ASSM_ID_I',estimate_percent=> null);

PL/SQL procedure successfully completed.

SQL> SELECT t.table_name, i.index_name, t.blocks, t.num_rows, i.clustering_factorFROM user_tables t, user_indexes iWHERE t.table_name = i.table_name AND i.index_name='BOWIE_ASSM_ID_I';

TABLE_NAME INDEX_NAME BLOCKS NUM_ROWS CLUSTERING_FACTOR---------- ---------------------- ---------- ---------- -----------------BOWIE_ASSM BOWIE_ASSM_ID_I 1000 300000 909

Clustering Factor reduced from 217236 to just 909 !!

2. TABLE_CACHED_BLOCKS

Page 25: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 25

SQL> select * from bowie_assm where id between 42 and 429;

388 rows selected.

Execution Plan-------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes |Cost (%CPU)| Time |-------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 389 | 6613 | 4 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| BOWIE_ASSM | 389 | 6613 | 4 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | BOWIE_ASSM_ID_I | 389 | | 2 (0)| 00:00:01 |-------------------------------------------------------------------------------------------------------

Statistics----------------------------------------------------------

0 recursive calls0 db block gets6 consistent gets0 physical reads0 redo size

8734 bytes sent via SQL*Net to client608 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)

388 rows processed

2. TABLE_CACHED_BLOCKS

Page 26: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 26

SQL> create table major_tom (id number, DOB date, text varchar2(42));

Table created.

SQL> insert into major_tom select rownum, sysdate-trunc(dbms_random.value(0, 20000)), 'DAVID BOWIE' from dual connect by level <= 2000000;

2000000 rows created.

SQL> commit;

Commit complete.

SQL> create index major_tom_dob_i on major_tom(dob);

Index created.

SQL> EXEC dbms_stats.gather_table_stats(ownname=>user, tabname=>'MAJOR_TOM', estimate_percent=> null, method_opt=>'FOR ALL COLUMNS SIZE 1');

PL/SQL procedure successfully completed.

SQL> SELECT t.table_name, i.index_name, t.blocks, t.num_rows, i.clustering_factorFROM user_tables t, user_indexes iWHERE t.table_name = i.table_name AND i.index_name='MAJOR_TOM_DOB_I';

TABLE_NAME INDEX_NAME BLOCKS NUM_ROWS CLUSTERING_FACTOR------------ --------------- ---------- ---------- -----------------MAJOR_TOM MAJOR_TOM_DOB_I 9077 2000000 1988249

2. TABLE_CACHED_BLOCKS

Page 27: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 27

SQL> exec dbms_stats.set_table_prefs(ownname=>user, tabname=>'MAJOR_TOM', pname=>'TABLE_CACHED_BLOCKS', pvalue=>255);

PL/SQL procedure successfully completed.

SQL> EXEC dbms_stats.gather_index_stats(ownname=>user, indname=>'MAJOR_TOM_DOB_I', estimate_percent=> null);

PL/SQL procedure successfully completed.

SQL> SELECT t.table_name, i.index_name, t.blocks, t.num_rows, i.clustering_factor2 FROM user_tables t, user_indexes i3 WHERE t.table_name = i.table_name AND i.index_name='MAJOR_TOM_DOB_I';

TABLE_NAME INDEX_NAME BLOCKS NUM_ROWS CLUSTERING_FACTOR------------ --------------- ---------- ---------- -----------------MAJOR_TOM MAJOR_TOM_DOB_I 9077 2000000 1941536

If the Clustering Factor truly is poor, even setting TABLE_CACHED_BLOCKS to the max of 255 quite correctly has little impact…

2. TABLE_CACHED_BLOCKS

Page 28: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 28

SQL> select * from major_tom where dob between '01-JUN-2017' and '30-JUN-2017';

2955 rows selected.

Execution Plan-------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 3100 | 77500 | 2484 (1)| 00:00:01 ||* 1 | TABLE ACCESS FULL| MAJOR_TOM | 3100 | 77500 | 2484 (1)| 00:00:01 |-------------------------------------------------------------------------------

Statistics----------------------------------------------------------

0 recursive calls0 db block gets

8582 consistent gets0 physical reads0 redo size

54489 bytes sent via SQL*Net to client608 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)

2955 rows processed

2. TABLE_CACHED_BLOCKS

Page 29: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 29

SQL> alter index bowie_assm_id_i rebuild;

Index altered.

SQL> select * from bowie_assm where id between 42 and 429;

388 rows selected.

Execution Plan--------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |--------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 389 | 6613 | 282 (11)| 00:00:01 ||* 1 | TABLE ACCESS FULL| BOWIE_ASSM | 389 | 6613 | 282 (11)| 00:00:01 |--------------------------------------------------------------------------------

Statistics----------------------------------------------------------

3 recursive calls0 db block gets

956 consistent gets0 physical reads0 redo size

4094 bytes sent via SQL*Net to client608 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)

388 rows processed

2. TABLE_CACHED_BLOCKS

Page 30: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 30

SQL> SELECT t.table_name, i.index_name, t.blocks, t.num_rows, i.clustering_factorFROM user_tables t, user_indexes iWHERE t.table_name = i.table_name AND i.index_name='BOWIE_ASSM_ID_I';

TABLE_NAME INDEX_NAME BLOCKS NUM_ROWS CLUSTERING_FACTOR---------- -------------------- ---------- ---------- -----------------BOWIE_ASSM BOWIE_ASSM_ID_I 1000 300000 217236

SQL> exec dbms_stats.set_schema_prefs(ownname=>user, pname=>'TABLE_CACHED_BLOCKS', pvalue=>42);

PL/SQL procedure successfully completed.

SQL> exec dbms_stats.set_database_prefs(pname=>'TABLE_CACHED_BLOCKS', pvalue=>42);

PL/SQL procedure successfully completed.

SQL> SELECT t.table_name, i.index_name, t.blocks, t.num_rows, i.clustering_factorFROM user_tables t, user_indexes iWHERE t.table_name = i.table_name AND i.index_name='BOWIE_ASSM_ID_I';

TABLE_NAME INDEX_NAME BLOCKS NUM_ROWS CLUSTERING_FACTOR---------- ---------------------- ---------- ---------- -----------------BOWIE_ASSM BOWIE_ASSM_ID_I 1000 300000 909

2. TABLE_CACHED_BLOCKS

Page 31: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 31

3. Clustering Attribute (12.1)

• In order to improve CF where data is truly scattered, need to reorder rows in table to match associated index

• Changing order of table to improve clustering of a column can impact clustering of other columns

• But it’s still possible to improve clustering of multiple columns concurrently, providing no column is too distinct

• Oracle 12.2 has made table reorgs much easier to implement

• OK, let's fix the “poor” Clustering Factor from our previous MAJOR_TOM example ...

Page 32: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 32

SQL> alter table major_tom add clustering by linear order(DOB) without materialized zonemap;

Table altered.

SQL> alter table major_tom move online;

Table altered.

SQL> SELECT t.table_name, i.index_name, t.blocks, t.num_rows, i.clustering_factorFROM user_tables t, user_indexes iWHERE t.table_name = i.table_name AND i.index_name='MAJOR_TOM_DOB_I';

TABLE_NAME INDEX_NAME BLOCKS NUM_ROWS CLUSTERING_FACTOR---------- ---------------------- ---------- ---------- -----------------MAJOR_TOM MAJOR_TOM_DOB_I 9077 2000000 8445

How to improve performance on the MAJOR_TOM table ?

Clustering Factor on the DOB column has dropped dramatically from 1941536 to 8445 …

3. Clustering Attribute

Page 33: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 33

SQL> select * from major_tom where dob between '01-JUN-2017' and '30-JUN-2017';

2955 rows selected.

Execution Plan------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes |Cost (%CPU)| Time |------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 3100 | 77500 | 25 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| MAJOR_TOM | 3100 | 77500 | 25 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | MAJOR_TOM_DOB_I | 3100 | | 11 (0)| 00:00:01 |------------------------------------------------------------------------------------------------------

Statistics----------------------------------------------------------

0 recursive calls0 db block gets25 consistent gets0 physical reads0 redo size

90805 bytes sent via SQL*Net to client608 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)

2955 rows processed

3. Clustering Attribute

Page 34: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 34

SQL> create table ziggy (id number, code number, grade number, name varchar2(42));

Table created.

SQL> insert into ziggy select rownum, mod(rownum, 100)+1, ceil(dbms_random.value(0,100)), 'ZIGGY STARDUST' from dual connect by level <=4000000;

4000000 rows created.

SQL> commit;

Commit complete.

SQL> create index ziggy_code_i on ziggy(code);

Index created.

SQL> create index ziggy_grade_i on ziggy(grade);

Index created.

SQL> select index_name, clustering_factor, num_rows from user_indexes where table_name='ZIGGY';

INDEX_NAME CLUSTERING_FACTOR NUM_ROWS---------------------- ----------------- ----------ZIGGY_CODE_I 1748800 4000000ZIGGY_GRADE_I 1572770 4000000

Both columns have relatively poor CF values as data values distributed throughout table

3. Clustering Attribute

Page 35: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 35

SQL> alter table ziggy add clustering by linear order (code);

Table altered.

SQL> alter table ziggy move online;

Table altered.

SQL> select index_name, clustering_factor, num_rows from user_indexes where table_name='ZIGGY';

INDEX_NAME CLUSTERING_FACTOR NUM_ROWS---------------------- ----------------- ----------ZIGGY_CODE_I 17560 4000000ZIGGY_GRADE_I 1577904 4000000

Clustering by just the CODE column improves its CF but has no impact on GRADE CF

3. Clustering Attribute

Page 36: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 36

SQL> alter table ziggy drop clustering;

Table altered.

SQL> alter table ziggy add clustering by interleaved order (code, grade);

Table altered.

SQL> alter table ziggy move online;

Table altered.

SQL> select index_name, clustering_factor, num_rows from user_indexes where table_name='ZIGGY';

INDEX_NAME CLUSTERING_FACTOR NUM_ROWS---------------------- ----------------- ----------ZIGGY_CODE_I 27358 4000000ZIGGY_GRADE_I 22563 4000000

Interleaved Clustering on both columns improves both CF (although the CF of CODE column is not quite as good compared to it clustered separately)

3. Clustering Attribute

Page 37: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 37

4. Index Tracking (12.2)SQL> create table radiohead (id number, code number, name varchar2(42));

Table created.

SQL> insert into radiohead select rownum, mod(rownum,10000), 'OK COMPUTER' from dual connect by level <= 1000000;

1000000 rows created.

SQL> commit;

Commit complete.

SQL> create index radiohead_code_i on radiohead(code);

Index created.

SQL> create unique index radiohead_id_i on radiohead(id);

Index created.

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

PL/SQL procedure successfully completed.

Page 38: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 38

SQL> select active_elem_count, alloc_elem_count, max_elem_count from v$index_usage_info;

ACTIVE_ELEM_COUNT ALLOC_ELEM_COUNT MAX_ELEM_COUNT----------------- ---------------- --------------

0 42 30000

SQL> select name, total_access_count, total_exec_count, total_rows_returnedfrom dba_index_usage where owner='RADIOHEAD';

no rows selected

SQL> select * from radiohead where id=42; ---- Execute 10 times

SQL> select * from radiohead where code=42; ---- Execute 10 times

SQL> select active_elem_count, alloc_elem_count, max_elem_count from v$index_usage_info;

ACTIVE_ELEM_COUNT ALLOC_ELEM_COUNT MAX_ELEM_COUNT----------------- ---------------- --------------

1 43 30000 ➔ Only one index has been picked up

Unique index usage with equality predicates are not captured

4. Index Tracking

Page 39: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 39

SQL> desc dba_index_usageName Null? Type----------------------------------------- -------- ----------------------------OBJECT_ID NOT NULL NUMBERNAME NOT NULL VARCHAR2(128)OWNER NOT NULL VARCHAR2(128)TOTAL_ACCESS_COUNT NUMBERTOTAL_EXEC_COUNT NUMBERTOTAL_ROWS_RETURNED NUMBERBUCKET_0_ACCESS_COUNT NUMBERBUCKET_1_ACCESS_COUNT NUMBERBUCKET_2_10_ACCESS_COUNT NUMBERBUCKET_2_10_ROWS_RETURNED NUMBERBUCKET_11_100_ACCESS_COUNT NUMBERBUCKET_11_100_ROWS_RETURNED NUMBERBUCKET_101_1000_ACCESS_COUNT NUMBERBUCKET_101_1000_ROWS_RETURNED NUMBERBUCKET_1000_PLUS_ACCESS_COUNT NUMBERBUCKET_1000_PLUS_ROWS_RETURNED NUMBERLAST_USED DATE

SQL> select name, total_exec_count, total_rows_returned, last_used from dba_index_usagewhere name like 'RADIOHEAD%';

NAME TOTAL_EXEC_COUNT TOTAL_ROWS_RETURNED LAST_USED-------------------- ---------------- ------------------- --------- ➔ Not all occurrences picked upRADIOHEAD_CODE_I 8 800 11-OCT-19

4. Index Tracking

Page 40: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 40

SQL> select * from radiohead where id between 42 and 420; ---- Execute 10 times

379 rows selected.

Execution Plan------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 380 | 7980 | 5 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| RADIOHEAD | 380 | 7980 | 5 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | RADIOHEAD_ID_I | 380 | | 3 (0)| 00:00:01 |------------------------------------------------------------------------------------------------------

SQL> select active_elem_count, alloc_elem_count, max_elem_count from v$index_usage_info;

ACTIVE_ELEM_COUNT ALLOC_ELEM_COUNT MAX_ELEM_COUNT----------------- ---------------- --------------

1 44 30000

SQL> select name, total_exec_count, total_rows_returned, last_used from dba_index_usagewhere name like 'RADIOHEAD%';

NAME TOTAL_EXEC_COUNT TOTAL_ROWS_RETURNED LAST_USED-------------------- ---------------- ------------------- ---------RADIOHEAD_CODE_I 8 800 11-OCT-19RADIOHEAD_ID_I 10 3790 11-OCT-19 <= Unique indexes captured for non

equality predicates

4. Index Tracking

Page 41: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 41

--- By default, the maximum index entries that can be tracked is 30000; however it can be increased with:

alter system set “_iux_max_entries=50000”;

--- To collect index usage stats for all the sql executions you can set the following parameter:

alter system set "_iut_stat_collection_type"=ALL;

--- For sampled, which is default you can set it as:

alter system set "_iut_stat_collection_type"=SAMPLED;

Has same rules regarding not capturing implicit usage of FK indexes and index statistics as with Index Monitoring

4. Index Tracking

Used by Automatic Indexing with default settings to determine when to drop an index

Page 42: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 42

SQL> create table ok_computer (id number, code varchar2(5), name varchar2(42), other varchar2(42));

Table created

SQL> insert into ok_computer select rownum, mod(rownum,10), 'David Bowie', 'Ziggy Stardust’ from dual connect by level <= 100;

100 rows created.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats(ownname=>null, tabname=>'OK_COMPUTER', estimate_percent=>null, method_opt=>'FOR ALL COLUMNS SIZE 1');

PL/SQL procedure successfully completed.

SQL> create unique index ok_computer_id_i on ok_computer(id);

Index created.

SQL> alter table ok_computer add primary key(id);

Table altered.

5. Drop Concatenated Index - Extended Stats

Page 43: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 43

SQL> create table kid_a (id number, code varchar2(5), name varchar2(42), other varchar2(42));

Table created

SQL> insert into kid_a select rownum, mod(rownum,10), 'Thin White Duke', 'Major Tom' from dual connect by level <= 100;

100 rows created.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats(ownname=>null, tabname=>'KID_A', estimate_percent=>null, method_opt=>'FOR ALL COLUMNS SIZE 1');

PL/SQL procedure successfully completed.

SQL> create unique index kid_a_id_i on kid_a(id);

Index created.

SQL> alter table kid_a add primary key(id);

Table altered.

5. Drop Concatenated Index - Extended Stats

Page 44: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 44

SQL> create table radiohead (id number, code1 number, code2 number, code3 number, code4 number, ok_fk number, kid_fk number, name varchar2(142));

Table created.

SQL> beginfor i in 1..100000 loop

insert into radiohead values(i, 1, 1, 1, 1, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 2, 2, 2, 2, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 3, 3, 3, 3, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 4, 4, 4, 4, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 5, 5, 5, 5, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 6, 6, 6, 6, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 7, 7, 7, 7, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 8, 8, 8, 8, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 9, 9, 9, 9, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 10, 10, 10, 10, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 11, 11, 11, 11, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 12, 12, 12, 12, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 13, 13, 13, 13, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 14, 14, 14, 14, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 15, 15, 15, 15, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 16, 16, 16, 16, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 17, 17, 17, 17, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 18, 18, 18, 18, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 19, 19, 19, 19, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');insert into radiohead values(i, 20, 20, 20, 20, ceil(dbms_random.value(0,100)), ceil(dbms_random.value(0,100)), 'David Bowie is Ziggy Stardust');

end loop;commit;

end;/

PL/SQL procedure successfully completed.

5. Drop Concatenated Index - Extended Stats

Page 45: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 45

SQL> alter table radiohead add constraint ok_fk_fk foreign key (ok_fk) references ok_computer(id);

Table altered.

SQL> alter table radiohead add constraint kid_fk_fk foreign key (kid_fk) references kid_a(id);

Table altered.

SQL> exec dbms_stats.gather_table_stats(ownname=>null, tabname=>'RADIOHEAD', estimate_percent=>null, method_opt=>'FOR ALL COLUMNS SIZE 1');

PL/SQL procedure successfully completed.

SQL> create index radiohead_id_i on radiohead(id);

Index created.

SQL> create index radiohead_ok_fk_i on radiohead(ok_fk);

Index created.

SQL> create index radiohead_kid_fk_i on radiohead(kid_fk);

Index created.

SQL> create index radiohead_codes_i on radiohead(code1, code2, code3, code4);

Index created.

SQL> select index_name, distinct_keys from user_indexes where table_name='RADIOHEAD';

INDEX_NAME DISTINCT_KEYS-------------------- -------------RADIOHEAD_ID_I 100000RADIOHEAD_OK_FK_I 100RADIOHEAD_KID_FK_I 100RADIOHEAD_CODES_I 20

5. Drop Concatenated Index - Extended Stats

Page 46: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 46

SQL> select * from radiohead r, ok_computer o, kid_a a where r.ok_fk=o.id and r.kid_fk=a.id and r.code1=1 and r.code2=1 and r.code3=1 and r.code4=1;

100000 rows selected.

Execution Plan-----------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-----------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 100K| 11M| 4163 (3)| 00:00:01 | <= Correct cardinality estimate|* 1 | HASH JOIN | | 100K| 11M| 4163 (3)| 00:00:01 || 2 | TABLE ACCESS FULL | KID_A | 100 | 3100 | 3 (0)| 00:00:01 ||* 3 | HASH JOIN | | 100K| 8300K| 4159 (3)| 00:00:01 || 4 | TABLE ACCESS FULL| OK_COMPUTER | 100 | 3200 | 3 (0)| 00:00:01 ||* 5 | TABLE ACCESS FULL| RADIOHEAD | 100K| 5175K| 4154 (3)| 00:00:01 | <= Correct cardinality estimate-----------------------------------------------------------------------------------

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

1 - access("R"."KID_FK"="A"."ID")3 - access("R"."OK_FK"="O"."ID")5 - filter("R"."CODE1"=1 AND "R"."CODE2"=1 AND "R"."CODE3"=1 AND "R"."CODE4"=1)

Statistics----------------------------------------------------------

8 recursive calls0 db block gets

16235 consistent gets0 physical reads0 redo size

2654640 bytes sent via SQL*Net to client718 bytes received via SQL*Net from client21 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)

100000 rows processed

5. Drop Concatenated Index - Extended Stats

Page 47: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 47

SQL> drop index radiohead_codes_i;

Index dropped.

5. Drop Concatenated Index - Extended Stats

Page 48: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 48

SQL> select * from radiohead r, ok_computer o, kid_a a where r.ok_fk=o.id and r.kid_fk=a.id and r.code1=1 and r.code2=1 and r.code3=1 and r.code4=1;

Execution Plan---------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 13 | 1508 | 4159 (3)| 00:00:01 | <= Incorrect cardinality estimate|* 1 | HASH JOIN | | 13 | 1508 | 4159 (3)| 00:00:01 || 2 | MERGE JOIN | | 13 | 1092 | 4156 (3)| 00:00:01 || 3 | TABLE ACCESS BY INDEX ROWID| KID_A | 100 | 3100 | 2 (0)| 00:00:01 || 4 | INDEX FULL SCAN | KID_A_ID_I | 100 | | 1 (0)| 00:00:01 ||* 5 | SORT JOIN | | 13 | 689 | 4154 (3)| 00:00:01 | <= Incorrect sort estimate|* 6 | TABLE ACCESS FULL | RADIOHEAD | 13 | 689 | 4153 (3)| 00:00:01 | <= Incorrect cardinality estimate| 7 | TABLE ACCESS FULL | OK_COMPUTER | 100 | 3200 | 3 (0)| 00:00:01 |---------------------------------------------------------------------------------------------

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

1 - access("R"."OK_FK"="O"."ID")5 - access("R"."KID_FK"="A"."ID")

filter("R"."KID_FK"="A"."ID")6 - filter("R"."CODE1"=1 AND "R"."CODE2"=1 AND "R"."CODE3"=1 AND "R"."CODE4"=1)

Statistics----------------------------------------------------------

0 recursive calls0 db block gets

16229 consistent gets0 physical reads0 redo size

2014913 bytes sent via SQL*Net to client718 bytes received via SQL*Net from client21 SQL*Net roundtrips to/from client1 sorts (memory)0 sorts (disk)

100000 rows processed

5. Drop Concatenated Index - Extended Stats

Page 49: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 49

SQL> exec dbms_stats.gather_table_stats(ownname=>null, tabname=>'RADIOHEAD', estimate_percent=>null, method_opt=>'FOR COLUMNS (CODE1,CODE2,CODE3,CODE4) SIZE AUTO');

PL/SQL procedure successfully completed.

SQL> select column_name, num_distinct, hidden_column, virtual_column from user_tab_colswhere table_name='RADIOHEAD';

COLUMN_NAME NUM_DISTINCT HID VIR------------------------------ ------------ --- ---ID 100824 NO NOCODE1 20 NO NOCODE2 20 NO NOCODE3 20 NO NOCODE4 20 NO NOOK_FK 100 NO NOKID_FK 100 NO NONAME 1 NO NOSYS_STUWBUJM8ROM348D4NYVVUMDWC 20 YES YES

9 rows selected.

5. Drop Concatenated Index - Extended Stats

Page 50: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 50

SQL> select * from radiohead r, ok_computer o, kid_a a where r.ok_fk=o.id and r.kid_fk=a.id and r.code1=1 and r.code2=1 and r.code3=1 and r.code4=1;

100000 rows selected.

Execution Plan-----------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-----------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 100K| 11M| 4163 (3)| 00:00:01 | <= Correct cardinality estimate|* 1 | HASH JOIN | | 100K| 11M| 4163 (3)| 00:00:01 || 2 | TABLE ACCESS FULL | KID_A | 100 | 3100 | 3 (0)| 00:00:01 ||* 3 | HASH JOIN | | 100K| 8300K| 4159 (3)| 00:00:01 || 4 | TABLE ACCESS FULL| OK_COMPUTER | 100 | 3200 | 3 (0)| 00:00:01 ||* 5 | TABLE ACCESS FULL| RADIOHEAD | 100K| 5175K| 4154 (3)| 00:00:01 | <= Correct cardinality estimate-----------------------------------------------------------------------------------

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

1 - access("R"."KID_FK"="A"."ID")3 - access("R"."OK_FK"="O"."ID")5 - filter("R"."CODE1"=1 AND "R"."CODE2"=1 AND "R"."CODE3"=1 AND "R"."CODE4"=1)

Statistics----------------------------------------------------------

8 recursive calls0 db block gets

16235 consistent gets0 physical reads0 redo size

2654640 bytes sent via SQL*Net to client718 bytes received via SQL*Net from client21 SQL*Net roundtrips to/from client0 sorts (memory)0 sorts (disk)

100000 rows processed

5. Drop Concatenated Index - Extended Stats

Page 51: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 51

6. Indexing Case In-Sensitive Database

SQL> create table bowie (id number, name varchar2(42), alias varchar2(42));

Table created.

SQL> insert into bowie select rownum, 'ZIGGY STARDUST'||rownum, 'ZIGGY'||rownumfrom dual connect by level <= 999995;

999995 rows created.

SQL> insert into bowie values (999996, 'David Bowie', 'Bowie');

1 row created.

SQL> insert into bowie values (999997, 'DAVID BOWIE', 'BOWIE');

1 row created.

SQL> insert into bowie values (999998, 'david bowie', 'bowie');

1 row created.

SQL> insert into bowie values (999999, 'David bowie', 'BowiE');

1 row created.

SQL> insert into bowie values (1000000, 'DaViD BoWiE', 'BoWiE');

1 row created.

SQL> commit;

Commit complete.

Page 52: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 52

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

PL/SQL procedure successfully completed.

SQL> create index bowie_name_i on bowie(name);

Index created.

SQL> select * from bowie where name='DAVID BOWIE';

ID NAME ALIAS---------- --------------- ----------

999997 DAVID BOWIE BOWIE

Execution Plan----------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |----------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 38 | 4 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| BOWIE | 1 | 38 | 4 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | BOWIE_NAME_I | 1 | | 3 (0)| 00:00:01 |----------------------------------------------------------------------------------------------------

6. Indexing Case In-Sensitive Database

Page 53: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 53

SQL> select * from bowie where upper(name)='DAVID BOWIE';

ID NAME ALIAS---------- --------------- ----------

999996 David Bowie Bowie999997 DAVID BOWIE BOWIE999998 david bowie bowie999999 David bowie BowiE1000000 DaViD BoWiE BoWiE

Execution Plan---------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 10000 | 371K| 1674 (10)| 00:00:01 ||* 1 | TABLE ACCESS FULL| BOWIE | 10000 | 371K| 1674 (10)| 00:00:01 |---------------------------------------------------------------------------

UPPER (or any other) function negates use of normal index…

6. Indexing Case In-Sensitive Database

Page 54: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 54

SQL> CREATE INDEX bowie_name_ci_i ON bowie(NLSSORT(name,'NLS_SORT=BINARY_CI'));

Index created.

SQL> ALTER SESSION SET NLS_SORT='BINARY_CI';

Session altered.

SQL> ALTER SESSION SET NLS_COMP='LINGUISTIC';

Session altered.

SQL> select * from bowie where name='DAVID BOWIE';

ID NAME ALIAS---------- --------------- ----------

999996 David Bowie Bowie999997 DAVID BOWIE BOWIE999998 david bowie bowie999999 David bowie BowiE1000000 DaViD BoWiE BoWiE

Execution Plan-------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes |Cost (%CPU)| Time |-------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 10000 | 253K| 1160 (1)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| BOWIE | 10000 | 253K| 1160 (1)| 00:00:01 ||* 2 | INDEX RANGE SCAN | BOWIE_NAME_CI_I | 4000 | | 1883 (1)| 00:00:01 |-------------------------------------------------------------------------------------------------------

A Linguistic Index can provide a case-insensitive option, without re-writing application

6. Indexing Case In-Sensitive Database

Page 55: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 55

SQL> create index bowie_alias_i on bowie(alias);

Index created.

SQL> select * from bowie where alias='BOWIE';

ID NAME ALIAS---------- --------------- ----------

999996 David Bowie Bowie999997 DAVID BOWIE BOWIE999998 david bowie bowie999999 David bowie BowiE1000000 DaViD BoWiE BoWiE

Execution Plan---------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 38 | 1750 (14)| 00:00:01 ||* 1 | TABLE ACCESS FULL| BOWIE | 1 | 38 | 1750 (14)| 00:00:01 |---------------------------------------------------------------------------

SQL> select index_name, index_type from user_indexes where table_name='BOWIE';

INDEX_NAME INDEX_TYPE------------------------------ ---------------------------BOWIE_NAME_I NORMALBOWIE_NAME_CI_I FUNCTION-BASED NORMALBOWIE_ALIAS_I NORMAL

Use of session/system parameters negates use of binary indexes and case-sensitive searches…

6. Indexing Case In-Sensitive Database

Page 56: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 56

SQL> create table ziggy (id number, name varchar2(42), alias varchar2(42) collate binary) default collation binary_ci;

Table created.

SQL> insert into ziggy select rownum, 'ZIGGY STARDUST'||rownum, 'ZIGGY'||rownumfrom dual connect by level <= 999995;

999995 rows created.

SQL> insert into ziggy values (999996, 'David Bowie', 'Bowie');

1 row created.

SQL> insert into ziggy values (999997, 'DAVID BOWIE', 'BOWIE');

1 row created.

SQL> insert into ziggy values (999998, 'david bowie', 'bowie');

1 row created.

SQL> insert into ziggy values (999999, 'David bowie', 'BowiE');

1 row created.

SQL> insert into ziggy values (1000000, 'DaViD BoWiE', 'BoWiE');

1 row created.

SQL> commit;

Commit complete.

6. Indexing Case In-Sensitive Database (12.2)

Page 57: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 57

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

PL/SQL procedure successfully completed.

SQL> create index ziggy_name_i on bowie(name);

Index created.

SQL> select * from ziggy where name='DAVID BOWIE';

ID NAME ALIAS---------- --------------- ----------

999996 David Bowie Bowie999997 DAVID BOWIE BOWIE999998 david bowie bowie999999 David bowie BowiE1000000 DaViD BoWiE BoWiE

Execution Plan----------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |----------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 38 | 4 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| ZIGGY | 1 | 38 | 4 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | ZIGGY_NAME_I | 1 | | 3 (0)| 00:00:01 |----------------------------------------------------------------------------------------------------

The NAME column is now automatically case in-sensitive with an implicit Linguistic Index…

6. Indexing Case In-Sensitive Database

Page 58: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 58

SQL> create index ziggy_alias_i on ziggy(alias);

Index created.

SQL> select * from ziggy where alias='BOWIE';

ID NAME ALIAS---------- --------------- ----------

999997 DAVID BOWIE BOWIE

Execution Plan-----------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-----------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 26 | 4 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| ZIGGY | 1 | 26 | 4 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | ZIGGY_ALIAS_I | 1 | | 3 (0)| 00:00:01 |-----------------------------------------------------------------------------------------------------

SQL> select index_name, index_type from user_indexes where table_name='ZIGGY';

INDEX_NAME INDEX_TYPE------------------------------ ---------------------------ZIGGY_NAME_I FUNCTION-BASED NORMALZIGGY_ALIAS_I NORMAL

The ALIAS column however remains case sensitive with an implicit Binary index …

6. Indexing Case In-Sensitive Database

Page 59: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 59

SQL> alter table ziggy default collation binary; ===➔ Only impacts new columns

Table altered.

SQL> select * from ziggy where name='DAVID BOWIE';

ID NAME ALIAS---------- --------------- ----------

999996 David Bowie Bowie999997 DAVID BOWIE BOWIE999998 david bowie bowie999999 David bowie BowiE1000000 DaViD BoWiE BoWiE

Execution Plan----------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |----------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 5 | 210 | 4 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| ZIGGY | 5 | 210 | 4 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | ZIGGY_NAME_I | 5 | | 3 (0)| 00:00:01 |----------------------------------------------------------------------------------------------------

SQL> alter user bowie default collation binary_ci;

User altered.

SQL> alter session set default_collation=BINARY_CI;

Session altered.

The default Collation can be changed at the session, database or schema levels

6. Indexing Case In-Sensitive Database

Page 60: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 60

7. Basic Index Compression

• Perhaps the most under-used and neglected index option

• Can potentially significantly reduce size of index segments

• Minimal overheads

• Can however be abused and cause problems

• Available as part of Enterprise Edition (does not require Advanced Compression)

CREATE INDEX big_bowie_i ON big_bowie(ziggy) COMPRESS;

Page 61: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 61

0: David Bowie: ROWID

1: David Bowie: ROWID

2: David Bowie: ROWID

3: David Bowie: ROWID

4: David Bowie: ROWID

5: David Jones: ROWID

6: David Jones: ROWID

7: David Jones: ROWID

...

Example of NOCOMPRESS Index Block

Each duplicate index entry value is repeated for each index row entry

7. Basic Index Compression

Page 62: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 62

Prefix 0: David Bowie0:0: ROWID

1: 0: ROWID

2: 0: ROWID

3: 0: ROWID

4: 0: ROWID

Prefix 1: David Jones5: 1: ROWID

6: 1: ROWID

7: 1: ROWID

...

Example of a COMPRESS Index Block

Each unique index entry value in block is stored in a Prefix table within leaf block

Each index entry then refers to the Prefix table entry so that each distinct index entry is only stored once per leaf block.

7. Basic Index Compression

Page 63: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 63

First an example index with no compression.

SQL> CREATE TABLE bowie_compress AS SELECT * FROM dba_tables;

Table created.

SQL> INSERT INTO bowie_compress SELECT * FROM bowie_compress;

2125 rows created.

SQL> INSERT INTO bowie_compress SELECT * FROM bowie_compress;

4250 rows created.

SQL> INSERT INTO bowie_compress SELECT * FROM bowie_compress;

8500 rows created.

SQL> INSERT INTO bowie_compress SELECT * FROM bowie_compress;

17000 rows created.

SQL> INSERT INTO bowie_compress SELECT * FROM bowie_compress;

34000 rows created.

SQL> commit;

Commit complete.

7. Basic Index Compression

Page 64: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 64

Create NOCOMPRESS index. In this example, the index has 2 columns and is Non-unique

SQL> CREATE INDEX bowie_compress_i ON bowie_compress(owner,table_name) PCTFREE 0;

Index created.

SQL> select index_name, blevel, leaf_blocks from user_indexeswhere table_name='BOWIE_COMPRESS';

INDEX_NAME BLEVEL LEAF_BLOCKS-------------------- ---------- -----------BOWIE_COMPRESS_I 2 285

7. Basic Index Compression

Page 65: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 65

kdxconro 218 <= Index block has 218 index entries …row#0[7997] flag: -------, lock: 0, len=39col 0; len 9; (9): 41 50 50 51 4f 53 53 59 53col 1; len 19; (19): 57 4c 4d 5f 43 4c 41 53 53 49 46 49 45 52 5f 50 4c 41 4ecol 2; len 6; (6): 01 c1 cf f5 00 0brow#1[7958] flag: -------, lock: 0, len=39col 0; len 9; (9): 41 50 50 51 4f 53 53 59 53col 1; len 19; (19): 57 4c 4d 5f 43 4c 41 53 53 49 46 49 45 52 5f 50 4c 41 4ecol 2; len 6; (6): 01 c1 d0 fc 00 02row#2[7919] flag: -------, lock: 0, len=39col 0; len 9; (9): 41 50 50 51 4f 53 53 59 53col 1; len 19; (19): 57 4c 4d 5f 43 4c 41 53 53 49 46 49 45 52 5f 50 4c 41 4ecol 2; len 6; (6): 01 c1 d1 02 00 02row#3[7880] flag: -------, lock: 0, len=39col 0; len 9; (9): 41 50 50 51 4f 53 53 59 53col 1; len 19; (19): 57 4c 4d 5f 43 4c 41 53 53 49 46 49 45 52 5f 50 4c 41 4ecol 2; len 6; (6): 01 c1 d1 76 00 0erow#4[7841] flag: -------, lock: 0, len=39col 0; len 9; (9): 41 50 50 51 4f 53 53 59 53col 1; len 19; (19): 57 4c 4d 5f 43 4c 41 53 53 49 46 49 45 52 5f 50 4c 41 4ecol 2; len 6; (6): 01 c1 d1 aa 00 02row#5[7802] flag: -------, lock: 0, len=39col 0; len 9; (9): 41 50 50 51 4f 53 53 59 53col 1; len 19; (19): 57 4c 4d 5f 43 4c 41 53 53 49 46 49 45 52 5f 50 4c 41 4ecol 2; len 6; (6): 01 c1 d2 19 00 10

Partial block dump

7. Basic Index Compression

Page 66: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 66

Let's now rebuild the index but with compression this time.

SQL> alter index bowie_compress_i rebuild compress;

Index altered.

SQL> select index_name, blevel, leaf_blocks from user_indexeswhere table_name='BOWIE_COMPRESS';

INDEX_NAME BLEVEL LEAF_BLOCKS-------------------- ---------- -----------BOWIE_COMPRESS_I 1 102

First thing we notice is how the index is now much smaller, enough to even reduce the overall BLEVEL of the index (down from 285 to 102 leaf blocks)

7. Basic Index Compression

Page 67: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 67

kdxconro 676 <= Index block has 676 index entries …prefix row#0[8004] flag: -P-----, lock: 0, len=32col 0; len 9; (9): 41 50 50 51 4f 53 53 59 53col 1; len 19; (19): 57 4c 4d 5f 43 4c 41 53 53 49 46 49 45 52 5f 50 4c 41 4eprc 32 <= 32 index entries reference this prefix entryprefix row#1[7686] flag: -P-----, lock: 0, len=30col 0; len 9; (9): 41 50 50 51 4f 53 53 59 53col 1; len 17; (17): 57 4c 4d 5f 46 45 41 54 55 52 45 5f 55 53 41 47 45prc 32...prefix row#21[1517] flag: -P-----, lock: 0, len=25 <= last prefix entry col 0; len 6; (6): 43 54 58 53 59 53col 1; len 15; (15): 44 52 24 46 45 41 54 55 52 45 5f 55 53 45 44prc 4row#0[7995] flag: -------, lock: 0, len=9col 0; len 6; (6): 01 c1 cf f5 00 0b <= index entry consists of just the rowidpsno 0row#1[7986] flag: -------, lock: 0, len=9col 0; len 6; (6): 01 c1 d0 fc 00 02psno 0 <= prefix row value associated with this index row entry …row#675[1481] flag: -------, lock: 0, len=9 <= last index entrycol 0; len 6; (6): 01 c1 d1 57 00 14psno 21

7. Basic Index Compression

Page 68: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 68

• Non-Unique Index: number of compressed columns by default is number of indexed columns

• Unique Index: number of compressed columns by default is number of indexed columns – 1

• COMPRESS integer determines how many columns are actually compressed

• Column order in index is critical to effectiveness of index compression

7. Basic Index Compression

Page 69: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 69

Compress just one column in index (with the very selective ID column listed second)

SQL> CREATE TABLE bowie_compress2 AS SELECT rownum id, bc.* FROM bowie_compress bc ORDER BY owner, num_rows;

Table created.

SQL> CREATE INDEX bowie_compress2_i ON bowie_compress2(owner,id) PCTFREE 0 COMPRESS 1;

Index created.

SQL> select index_name, blevel, leaf_blocks from user_indexeswhere table_name='BOWIE_COMPRESS2';

INDEX_NAME BLEVEL LEAF_BLOCKS-------------------- ---------- -----------BOWIE_COMPRESS2_I 1 136

7. Basic Index Compression

Page 70: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 70

kdxconro 502 <= Index block has 676 index entries …prefix row#0[8024] flag: -P-----, lock: 0, len=12col 0; len 9; (9): 41 50 50 51 4f 53 53 59 53prc 160 <= 160 index entries reference this prefix entry…prefix row#3[1357] flag: -P-----, lock: 0, len=9 <= Only 4 prefix entries in this index blockcol 0; len 6; (6): 43 54 58 53 59 53prc 22row#0[8011] flag: -------, lock: 0, len=13col 0; len 3; (3): c2 11 04 <= Index entries consist of ID and Rowidcol 1; len 6; (6): 01 c1 d0 2f 00 0epsno 0 <= This index entry references prefix entry 0 row#1[7998] flag: -------, lock: 0, len=13col 0; len 3; (3): c2 11 05 col 1; len 6; (6): 01 c1 d0 2f 00 0fpsno 0row#2[7985] flag: -------, lock: 0, len=13col 0; len 3; (3): c2 11 06col 1; len 6; (6): 01 c1 d0 2f 00 10psno 0...row#501[1071] flag: -------, lock: 0, len=13 <= Last index entry in blockcol 0; len 3; (3): c2 12 4acol 1; len 6; (6): 01 c1 d0 7f 00 03psno 3 <= Last index entry references prefix entry 3

7. Basic Index Compression

Page 71: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 71

Compress just one column in index (with the very selective ID column now listed first)

SQL> CREATE INDEX bowie_compress2_i2 ON bowie_compress2(id,owner) PCTFREE 0 COMPRESS 1;

Index created.

SQL> select index_name, blevel, leaf_blocks from user_indexeswhere table_name='BOWIE_COMPRESS2';

INDEX_NAME BLEVEL LEAF_BLOCKS-------------------- ---------- -----------BOWIE_COMPRESS2_I2 1 229BOWIE_COMPRESS2_I 1 136

Note that the number of leaf blocks has now jumped considerably from 136 to 229 !!

7. Basic Index Compression

Page 72: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 72

kdxconro 323 <= Index block has 323 index entries …prefix row#0[8031] flag: -P-----, lock: 0, len=5col 0; len 2; (2): c1 02prc 1 <= Each prefix entry is reference by only 1 index entryprefix row#1[8013] flag: -P-----, lock: 0, len=5col 0; len 2; (2): c1 03prc 1…prefix row#322[2008] flag: -P-----, lock: 0, len=6 <= 323 prefix entries col 0; len 3; (3): c2 04 18prc 1row#0[8018] flag: -------, lock: 0, len=13col 0; len 3; (3): 53 59 53 <= Each index entry has duplicate OWNER columns col 1; len 6; (6): 01 c1 e5 3e 00 09psno 0 <= Each index entry references a different prefix entryrow#1[8000] flag: -------, lock: 0, len=13col 0; len 3; (3): 53 59 53col 1; len 6; (6): 01 c1 e6 c3 00 01psno 1...row#322[1995] flag: -------, lock: 0, len=13col 0; len 3; (3): 53 59 53col 1; len 6; (6): 01 c1 df 03 00 11psno 322 <= Last index entry references prefix entry 322

7. Basic Index Compression

Page 73: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 73

In fact, if you compare it with no compression at all ...

SQL> alter index bowie_compress2_i2 rebuild nocompress;

Index altered.

SQL> select index_name, blevel, leaf_blocks from user_indexeswhere table_name='BOWIE_COMPRESS2';

INDEX_NAME BLEVEL LEAF_BLOCKS-------------------- ---------- -----------BOWIE_COMPRESS2_I2 1 178BOWIE_COMPRESS2_I 1 136

Leaf Blocks drops down from 229 to 178 ...

Index with ID the leading column can NOT be effectively compressed, index larger not smaller

7. Basic Index Compression

Page 74: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 74

• Index column order can be crucial for compression effectiveness

• The more repeated the compressed column(s), the better the compression (“deduplication”) rate

• The higher the ratio of index row entries to prefix row entries, the better the compression rate

• The larger the repeated column values, the better the compression rate

• If the number of prefix entries is close to index entries, compression can increase index size due to prefix table overheads

7. Basic Index Compression

Page 75: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 75

How to determine best COMPRESS integer for existing indexes ?

SQL> drop index bowie_compress2_uk;

Index dropped.

SQL> CREATE INDEX bowie_compress2_i ON bowie_compress2(owner,id) pctfree 0;

Index created.

SQL> analyze index bowie_compress2_i validate structure;

Index analyzed.

SQL> SELECT name, height, lf_blks, opt_cmpr_count, opt_cmpr_pctsaveFROM index_stats;

NAME HEIGHT LF_BLKS OPT_CMPR_COUNT OPT_CMPR_PCTSAVE-------------------- ---------- ---------- -------------- ----------------BOWIE_COMPRESS2_I 2 178 1 23

7. Basic Index Compression

Page 76: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 76

Let's follow the recommendation:

SQL> alter index bowie_compress2_i rebuild compress 1;

Index altered.

SQL> analyze index bowie_compress2_i validate structure;

Index analyzed.

SQL> SELECT name, height, lf_blks, opt_cmpr_count, opt_cmpr_pctsaveFROM index_stats;

NAME HEIGHT LF_BLKS OPT_CMPR_COUNT OPT_CMPR_PCTSAVE-------------------- ---------- ---------- -------------- ----------------BOWIE_COMPRESS2_I 2 136 1 0

Reduced from 178 to 136 leaf blocks, a 23.6% reduction, very close to predication.

7. Basic Index Compression

Page 77: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 77

What's the advice for the index with ID as the leading column:

SQL> CREATE INDEX bowie_compress2_i2 ON bowie_compress2(id,owner) pctfree 0;

Index created.

SQL> analyze index bowie_compress2_i2 validate structure;

Index analyzed.

SQL> SELECT name, height, lf_blks, opt_cmpr_count, opt_cmpr_pctsaveFROM index_stats;

NAME HEIGHT LF_BLKS OPT_CMPR_COUNT OPT_CMPR_PCTSAVE-------------------- ---------- ---------- -------------- ----------------BOWIE_COMPRESS2_I2 2 178 0 0

Correctly recommends not compressing any of the columns on such an index.

7. Basic Index Compression

Page 78: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 78

Create a table with lots of common status values and the odd uncommon one

SQL> CREATE TABLE bowie_stuff (id number, status varchar2(42), description varchar2(42));

Table created.

SQL> INSERT INTO bowie_stuff SELECT rownum, 'PROCESSED', 'NOT REALLY INTERESTED WITH THIS ROW' FROM DUAL CONNECT BY LEVEL <= 1000000;

Table created.

SQL> UPDATE bowie_stuff SET status='BOWIE', description='ROW OF INTEREST' WHERE id > 999990;

10 row updated.

SQL> COMMIT;

Commit complete.

SQL> SELECT status, count(*) FROM bowie_stuff GROUP BY status;

STATUS COUNT(*)------------ ----------PROCESSED 999990BOWIE 10

8. Zero Sized Unusable Indexes

Page 79: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 79

SQL> CREATE INDEX bowie_stuff_i_1 ON bowie_stuff(status);

Index created.

SQL> exec dbms_stats.gather_table_stats(ownname=>'BOWIE', tabname=> 'BOWIE_STUFF', method_opt=> 'FOR COLUMNS STATUS SIZE 5');

PL/SQL procedure successfully completed.

SQL> SELECT * FROM bowie_stuff WHERE status = 'BOWIE';

Execution Plan-------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes |Cost (%CPU)| Time |-------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 10 | 460 | 4 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| BOWIE_STUFF | 10 | 460 | 4 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | BOWIE_STUFF_I_1 | 10 | | 3 (0)| 00:00:01 |-------------------------------------------------------------------------------------------------------

Statistics----------------------------------------------------------

6 consistent gets10 rows processed

8. Zero Sized Unusable Indexes

Page 80: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 80

But why bother indexing all the common values when they'll never be referenced ?

SQL> CREATE INDEX bowie_stuff_i_2 ON bowie_stuff(DECODE(status, 'BOWIE', 'BOWIE', NULL));

Index created.

SQL> exec dbms_stats.gather_table_stats(ownname=>'BOWIE', tabname=> 'BOWIE_STUFF', method_opt=>'FOR ALL HIDDEN COLUMNS SIZE 1', cascade=> true);

PL/SQL procedure successfully completed.

SQL> SELECT index_name, blevel, leaf_blocks, distinct_keys, num_rowsFROM user_indexes WHERE owner='BOWIE' AND table_name='BOWIE_STUFF';

INDEX_NAME BLEVEL LEAF_BLOCKS DISTINCT_KEYS NUM_ROWS-------------------- ---------- ----------- ------------- ----------BOWIE_STUFF_I_1 2 2924 2 1000000BOWIE_STUFF_I_2 0 1 1 10

Index is a fraction the size of previous index as it only contains 10 index entries…

8. Zero Sized Unusable Indexes

Page 81: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 81

SQL> SELECT * FROM bowie_stuff WHERE(DECODE(status, 'BOWIE', 'BOWIE', null)) = 'BOWIE';

Execution Plan-------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes |Cost (%CPU)| Time |-------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 10 | 480 | 2 (0)| 00:00:01 || 1 | TABLE ACCESS BY INDEX ROWID BATCHED| BOWIE_STUFF | 10 | 480 | 2 (0)| 00:00:01 ||* 2 | INDEX RANGE SCAN | BOWIE_STUFF_I_2 | 10 | | 1 (0)| 00:00:01 |-------------------------------------------------------------------------------------------------------

Statistics----------------------------------------------------------

4 consistent gets10 rows processed

Note: consistent gets have now reduced from 6 to 4DMLs are cheaper as well if updated column values do not need to be indexedBUT application must be changed to make use of such function-based indexes…

8. Zero Sized Unusable Indexes

Page 82: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 82

SQL> drop index bowie_stuff_i;

Index dropped.

SQL> create index bowie_stuff_i on bowie_stuff(status)global partition by range (status)(partition not_processed_part values less than (‘PROCESSED'),partition processed_part values less than (MAXVALUE))unusable;

Index created.

SQL> alter index bowie_stuff_i rebuild partition not_processed_part;

Index altered.

Can recreate the index as a partitioned index with only “useful” index partition usable.

Not reliant on how table is partitioned as with Partial Indexes…

8. Zero Sized Unusable Indexes (11.2)

Page 83: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 83

SQL> select index_name, partition_name, leaf_blocks from user_ind_partitionswhere index_name = 'BOWIE_STUFF_I';

INDEX_NAME PARTITION_NAME LEAF_BLOCKS-------------------- -------------------- -----------BOWIE_STUFF_I PROCESSED_PART 0BOWIE_STUFF_I NOT_PROCESSED_PART 1

We now only use a fraction of the storage for the index and the “useful” portion of the indexed data is just a single leaf block in size ...

8. Zero Sized Unusable Indexes

Page 84: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 84

SQL> select * from bowie_stuff where status = 'BOWIE';

Execution Plan----------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |----------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 10 | 460 | 1 (0)| 00:00:01 | | || 1 | PARTITION RANGE SINGLE | | 10 | 460 | 1 (0)| 00:00:01 | 1 | 1 || 2 | TABLE ACCESS BY INDEX ROWID BATCHED| BOWIE_STUFF | 10 | 460 | 1 (0)| 00:00:01 | | ||* 3 | INDEX RANGE SCAN | BOWIE_STUFF_I | 10 | | 1 (0)| 00:00:01 | 1 | 1 |----------------------------------------------------------------------------------------------------------------------

Statistics----------------------------------------------------------

4 consistent gets10 rows processed

The query itself is also more efficient with consistent gets again reduced from 6 down to 4.

And no need to re-write query with DECODE function.

8. Zero Sized Unusable Indexes

Page 85: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 85

9. Determine Optimal Index Size

SQL> create table major_tom as select o.* from dba_objects o, dba_users;

Table created.

SQL> exec dbms_stats.gather_table_stats(ownname=>user, tabname=>'MAJOR_TOM');

PL/SQL procedure successfully completed.

SQL> select count(*) from major_tom;

COUNT(*)----------

2776052

Common question: Are indexes currently optimally sized?

Page 86: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 86

SQL> explain plan for create index major_tom_object_name_i on major_tom(object_name);

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT-------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost(%CPU)| Time |-------------------------------------------------------------------------------------------------| 0 | CREATE INDEX STATEMENT | | 2776K| 92M| 1781 (1)| 00:00:01 || 1 | INDEX BUILD NON UNIQUE| MAJOR_TOM_OBJECT_NAME_I | | | | || 2 | SORT CREATE INDEX | | 2776K| 92M| | || 3 | TABLE ACCESS FULL | MAJOR_TOM | 2776K| 92M| 1479 (1)| 00:00:01 | |------------------------------------------------------------------------------------------------

Note-----

- estimated index size: 150M bytes

9. Determine Optimal Index Size

Page 87: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 87

SQL> create index major_tom_object_name_i on major_tom(object_name);

Index created.

SQL> select bytes from user_segments where segment_name='MAJOR_TOM_OBJECT_NAME_I';

BYTES----------150994944

SQL> analyze index major_tom_object_name_i validate structure;

Index analyzed.

SQL> select btree_space from index_stats;

BTREE_SPACE-----------142771616

Dependent on reasonably accurate table-level statistics

Assumes index entries are fully populated (no NULL entries)

9. Determine Optimal Index Size

Page 88: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 88

SQL> select count(*) from major_tom;

COUNT(*)----------

2776052SQL> select count(*) from major_tom where subobject_name is not null;

COUNT(*)----------

39140

SQL> explain plan for create index major_tom_subobject_name_i on major_tom(subobject_name);

Explained.-----------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-----------------------------------------------------------------------------------------------------| 0 | CREATE INDEX STATEMENT | | 2776K| 5421K| 16198 (1)| 00:00:01 || 1 | INDEX BUILD NON UNIQUE| MAJOR_TOM_SUBOBJECT_NAME_I | | | | || 2 | SORT CREATE INDEX | | 2776K| 5421K| | || 3 | TABLE ACCESS FULL | MAJOR_TOM | 2776K| 5421K| 14793 (1)| 00:00:01 |-----------------------------------------------------------------------------------------------------Note-----

- estimated index size: 67M bytes

SQL> create index major_tom_subobject_name_i on major_tom(subobject_name);

Index created.

SQL> select bytes from user_segments where segment_name='MAJOR_TOM_SUBOBJECT_NAME_I';

BYTES----------

2097152

Estimate is off as it assumes no NULLS (can still be approx. derived based on % of NULL values)

9. Determine Optimal Index Size

Page 89: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 89

SQL> insert into major_tom select * from major_tom;

2776052 rows created.

SQL> commit;

Commit complete.

SQL> explain plan for create index major_tom_object_name_i on major_tom(object_name);

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT-------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost(%CPU)| Time |-------------------------------------------------------------------------------------------------| 0 | CREATE INDEX STATEMENT | | 2776K| 92M| 1781 (1)| 00:00:01 || 1 | INDEX BUILD NON UNIQUE| MAJOR_TOM_OBJECT_NAME_I | | | | || 2 | SORT CREATE INDEX | | 2776K| 92M| | || 3 | TABLE ACCESS FULL | MAJOR_TOM | 2776K| 92M| 1479 (1)| 00:00:01 |-------------------------------------------------------------------------------------------------

Note-----

- estimated index size: 150M bytes

Estimated index size remains the same as before as table statistics are not current …

9. Determine Optimal Index Size

Page 90: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 90

SQL> exec dbms_stats.gather_table_stats(ownname=>user, tabname=>'MAJOR_TOM');

PL/SQL procedure successfully completed.

SQL> explain plan for create index major_tom_object_name_i on major_tom(object_name);

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost(%CPU)| Time |--------------------------------------------------------------------------------------------------| 0 | CREATE INDEX STATEMENT | | 5552K| 185M| 35462 (1)| 00:00:02 || 1 | INDEX BUILD NON UNIQUE| MAJOR_TOM_OBJECT_NAME_I | | | | || 2 | SORT CREATE INDEX | | 5552K| 185M| | || 3 | INDEX FAST FULL SCAN| MAJOR_TOM_OBJECT_NAME_I | | | | |--------------------------------------------------------------------------------------------------Note-----

- estimated index size: 293M bytes

The estimated index size is now accurate again …

9. Determine Optimal Index Size

Page 91: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 91

10. Multiple Indexes On Same Column List (12.1)

Multiple indexes can be created on the same set of columns as long as some characteristic is different. Qualifying characteristics include:

• B-tree versus bitmap• Different partitioning strategies• Unique versus Non-unique

Providing the capability to create multiple indexes on the same set of columns enables seamless application changes without the need to drop an existing index and re-create it with different attributes.

Page 92: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 92

SQL> create table bowie (id number, name varchar2(42))partition by range (id) (partition bowie1 values less than (1000),partition bowie2 values less than (2000), partition bowie3 values less than (maxvalue));

Table created.

SQL> create index bowie_id_i1 on bowie(id);

Index created.

SQL> alter table bowie add primary key(id);

Table altered.

SQL> create unique index bowie_id_i2 on bowie(id);create unique index bowie_id_i2 on bowie(id)

*ERROR at line 1:ORA-01408: such column list already indexed

SQL> create unique index bowie_id_i2 on bowie(id) invisible;

ERROR at line 1:ORA-01408: such column list already indexed

10. Multiple Indexes On Same Column ListPre 12.1

Page 93: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 93

SQL> drop index bowie_id_i1;drop index bowie_id_i1

*ERROR at line 1:ORA-02429: cannot drop index used for enforcement of unique/primary key

SQL> alter table bowie drop primary key;

Table altered.

SQL> drop index bowie_id_i1;

Index dropped.

SQL> alter table bowie add primary key(id) using index (create unique index bowie_id_i2 on bowie(id));

Table altered.

10. Multiple Indexes On Same Column List

Pre 12.1

Page 94: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 94

SQL> create unique index bowie_id_i2 on bowie(id) invisible;

Index created.

SQL> alter index bowie_id_i2 visible;alter index bowie_id_i2 visible*ERROR at line 1:ORA-14147: There is an existing VISIBLE index defined on the same set of columns.

SQL> alter table bowie modify primary key using index bowie_id_i2;

Table altered.

SQL> alter index bowie_id_i1 invisible;

Index altered.

SQL> alter index bowie_id_i2 visible;

Index altered.

Additional indexes with same column list can be created from 12c, provided only one is visible

10. Multiple Indexes On Same Column List

Page 95: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 95

SQL> create unique index bowie_id_i2 on bowie(id) invisible;

Index created.

SQL> create index bowie_id_i3 on bowie(id) local invisible;

Index created.

SQL> create bitmap index bowie_id_i4 on bowie(id) local invisible;

Index created.

SQL> create index bowie_id_i5 on bowie(id) reverse invisible;

Index created.

All these indexes can co-exist from 12c (but not a good idea …)

10. Multiple Indexes On Same Column List

Page 96: 10 Things You May Not Know About Oracle Indexes …...Oracle systems, tuned numerous databases often with 10x performance improvements • Oracle OakTable Member since 2002 and awarded

© RICHARD FOOTE CONSULTING 96

“Oracle Indexing Internals & Best Practices” 2 Day Seminar

LONDON: 23-24 March 2020Of benefit to DBAs, Developers, Solution Architects and anyone else interested in designing, developing or maintaining high performance Oracle-based applications/databases.

• Examines most available index structures/options & discusses in considerable detail how indexes function, how/when they should be used & how they should be maintained.

• Details how indexes are costed & evaluated by the Cost Based Optimizer (CBO) & how appropriate data management practices are vital for an effective indexing strategy.

• Covers many useful tips and strategies to maximise the benefits of indexes on application/database performance & scalability.

richardfooteconsulting.com/indexing-webinar/


Recommended