that had nothing to do with hash partitioning - I'll show you queries where the optimizer decided to stop using an index for less than 2% of the data (rightly so) and the table wasn't partitioned at all. The choice to use an index (or not) has to do with estimated cardinalities and how the data is layed out on DISK (clustering factors and the like)
hash partitioning could have dramatically affected the clustering factor of some indexes however, but it was not - is not that "hash partitioning makes the optimizer not use indexes". See below, at the bottom for an example
HASH partitioning is good for the following:
o higher availability (if you have 128 hash partitions and query "where key = ?" a lot, and a partition is offline - you have only a 1 in 128 chance of hitting an error, many people can continue to function)
o higher concurrency, if you have a table inserted into frequently, and use a sequence to populate an artificial key, you'll have a really hot right hand side index - lots of waits on that right hand side to keep inserting, decreased ability to scale. So, you hash it (the table) by primary key into 128 partitions and locally partition the primary key index. Now you have 128 "luke warm" right hand sides of indexes - greater ability to scale. And you would globally partition the remaining indexes that are inserted into randomly - the ones on last_name and the like - they can either be NOT partitioned or partitioned in any way you like - just do not locally partition them of course as you would have 128 index range scans for a query like "where last_name = ?"
o ease of administration - if for whatever reason you wanted to reorganize the table, you could do so in bite sized pieces, doing 1/128th of the work (continuing to use 128 from above, 128 is not magic number, just a number I picked to converse with here...)
disk contention is something I'd rather fix with ------- disks. RAID or ASM (which gives us the striping of RAID)
1) you can a) exchange with empty table, so as to ARCHIVE the table before dropping it. b) truncate old partition - just nuke the data. c) drop the partition - just nuke the data. What you do is up to you.
2) why would the run time environment "bother" with them at all. runtime environment would hopefully have something in its predicate so as to make it so that is ONLY VISITS DATA OF INTEREST. This is the point that always confuses ME. A big table is as efficient as a small table - the fact that there is 77 years of data matters not a bit to me - because I only access data of interest. The runtime system should never (by design) visit these partitions if there is no data of interest there. We use predicates on dates to eliminate partitions from consideration, we use predicates on other attributes to eliminate index partitions from consideration and so on.
3) global partitioned indexes are not likely to decrease access times in general. You *might* decrease an index from 4 to 3 - which would decrease the amount of IO's we do to get to a key - which would be good. In short, you would have to analyze your data to see if there would be any benefit to partitioning the indexes.
Now, looking at clustering factors:
ops$tkyte%ORA10GR2> create table organized
2 as
3 select x.*
4 from (select * from stage order by object_name) x
5 /
Table created.
ops$tkyte%ORA10GR2> create table disorganized
2 as
3 select x.*
4 from (select * from stage order by dbms_random.random) x
5 /
Table created.
ops$tkyte%ORA10GR2> create index organized_idx on organized(object_name);
Index created.
ops$tkyte%ORA10GR2> create index disorganized_idx on disorganized(object_name);
Index created.
ops$tkyte%ORA10GR2> begin
2 dbms_stats.gather_table_stats
3 ( user, 'ORGANIZED',
4 estimate_percent => 100,
5 method_opt=>'for all indexed columns size 254'
6 );
7 dbms_stats.gather_table_stats
8 ( user, 'DISORGANIZED',
9 estimate_percent => 100,
10 method_opt=>'for all indexed columns size 254'
11 );
12 end;
13 /
PL/SQL procedure successfully completed.
<b>those two tables contain the same bits and bytes - just in different orders on disk....</b>
ops$tkyte%ORA10GR2> select table_name, blocks, num_rows, 0.05*num_rows, 0.10*num_rows from user_tables
2 where table_name like '%ORGANIZED' order by 1;
TABLE_NAME BLOCKS NUM_ROWS 0.05*NUM_ROWS 0.10*NUM_ROWS
------------------------------ ---------- ---------- ------------- -------------
DISORGANIZED 707 49926 2496.3 4992.6
ORGANIZED 707 49926 2496.3 4992.6
ops$tkyte%ORA10GR2> select table_name, index_name, clustering_factor from user_indexes
2 where table_name like '%ORGANIZED' order by 1;
TABLE_NAME INDEX_NAME CLUSTERING_FACTOR
------------------------------ ------------------------------ -----------------
DISORGANIZED DISORGANIZED_IDX 49858
ORGANIZED ORGANIZED_IDX 687
<b>same number of rows - 5% of the data would be 2,500 records. 2.5% would be about 1,250 rows...
note the clustering factors. for the disorganized table - the clustering factor is near the number of rows in the table (hence the table is NOT sorted on disk in the same way the index keys are in the index). For the organized table, the clustering factor is near the number of blocks - hence the table data is sorted rather nicely by the indexed key values on disk - makes sense, we loaded the table sorted by the index key</b>
ops$tkyte%ORA10GR2> set autotrace traceonly explain
ops$tkyte%ORA10GR2> select * from organized where object_name like 'X%';
Execution Plan
----------------------------------------------------------
Plan hash value: 1925627673
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CP
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 68 | 6324 | 3 (
| 1 | TABLE ACCESS BY INDEX ROWID| ORGANIZED | 68 | 6324 | 3 (
|* 2 | INDEX RANGE SCAN | ORGANIZED_IDX | 68 | | 2 (
-------------------------------------------------------------------------------
ops$tkyte%ORA10GR2> select * from disorganized where object_name like 'X%';
Execution Plan
----------------------------------------------------------
Plan hash value: 3767053355
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 68 | 6324 | 71
| 1 | TABLE ACCESS BY INDEX ROWID| DISORGANIZED | 68 | 6324 | 71
|* 2 | INDEX RANGE SCAN | DISORGANIZED_IDX | 68 | | 2
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_NAME" LIKE 'X%')
filter("OBJECT_NAME" LIKE 'X%')
<b>when selecting 68 rows (0.13%) both tables use the index - but look at the cost - the cost is a function of the number of blocks Oracle expects to visit (in addition to other things, but the number of blocks is a big part of it)</b>
ops$tkyte%ORA10GR2> select * from organized where object_name like 'A%';
Execution Plan
----------------------------------------------------------
Plan hash value: 1925627673
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CP
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1125 | 102K| 23 (
| 1 | TABLE ACCESS BY INDEX ROWID| ORGANIZED | 1125 | 102K| 23 (
|* 2 | INDEX RANGE SCAN | ORGANIZED_IDX | 1125 | | 7 (
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_NAME" LIKE 'A%')
filter("OBJECT_NAME" LIKE 'A%')
ops$tkyte%ORA10GR2> select * from disorganized where object_name like 'A%';
Execution Plan
----------------------------------------------------------
Plan hash value: 2727546897
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1125 | 102K| 224 (3)| 00:00:0
|* 1 | TABLE ACCESS FULL| DISORGANIZED | 1125 | 102K| 224 (3)| 00:00:0
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OBJECT_NAME" LIKE 'A%')
<b>but when selecting 1,125 rows - a mere 2.25% - we see different plans. Same data, no partitioning involved, just different layout on disk...