(on a 38 million record table, 1 million rows is 1/38th of the data or about 2.6%, that would be selective....)
but if you can add columns to the index to reduce the table fetch by index rowid and a filter - that would be GREAT. for example:
ops$tkyte%ORA10GR2> create table t
2 as
3 select mod(rownum,38) id, a.*
4 from (select * from all_objects order by dbms_random.random) a
5 /
Table created.
ops$tkyte%ORA10GR2> exec dbms_stats.gather_table_stats( user, 'T' );
PL/SQL procedure successfully completed.
ops$tkyte%ORA10GR2> create index t_idx1 on t(id,owner);
Index created.
ops$tkyte%ORA10GR2> create index t_idx2 on t(id,owner,object_name);
Index created.
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> set termout off
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> set autotrace traceonly
ops$tkyte%ORA10GR2> select /*+ index( t, t_idx1 ) */ * from t where id = 0 and owner = 'SYS' and object_name = 'X';
no rows selected
Execution Plan
----------------------------------------------------------
Plan hash value: 4055207394
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 96 | 70 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 96 | 70 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_IDX1 | 82 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OBJECT_NAME"='X')
2 - access("ID"=0 AND "OWNER"='SYS')
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
496 consistent gets
0 physical reads
0 redo size
1047 bytes sent via SQL*Net to client
374 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
ops$tkyte%ORA10GR2> select /*+ index( t, t_idx2 ) */ * from t where id = 0 and owner = 'SYS' and object_name = 'X';
no rows selected
Execution Plan
----------------------------------------------------------
Plan hash value: 3371054274
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 96 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 96 | 3 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_IDX2 | 1 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=0 AND "OWNER"='SYS' AND "OBJECT_NAME"='X')
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
1047 bytes sent via SQL*Net to client
374 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
ops$tkyte%ORA10GR2> set autotrace off
the first index - t_idx1 - is "OK"
the second index - t_idx2 - is "PERFECTION"
I cannot address the "same column on so many indexes", you would have to verify that the index is actually used - that you need "COLUMN1, X" and "COLUMN1, Y" and "COLUMN1, Z". The only thing you would look for would be indexes that are not used.
This column is pretty selective - 1/9th of the data is about 11%. When you start adding in other attributes - the INDEX becomes even more selective (and that is what counts, the index selectivity - not the selectivity of the individual attributes)