Vous êtes sur la page 1sur 180

Oracle Partitioning, Complete Reference

_______________________________________________________________________
Advantages

Benefits of Oracle8 Partitioning - An Overview


The primary benefit of the partitioning option is ease of maintenance on very large tables
and improved reliability. This is important for both OLTP and data warehousing
environments. Partitioning also provides performance improvements via partition
elimination.

This section presents an overview of the benefits to the Database Administrator of using
the Oracle8 table partitioning option. This is especially useful for data warehouses with
very large tables, which typically present problems regarding maintenance and
performance. These two key areas are addressed by table partitioning.

Partition Basics
A table may be defined with multiple partitions based on a range of data values. All
partitions will have the same logical attributes, but may have different physical attributes
to allow different storage such as PCTFREE, PCTUSED, INITRANS, MAXTRANS. It
is recommended to store each partition in a separate tablespace which allows for
individual backup and recovery of each partition.

The following is an example of a table containing sales data partitioned by date for each
quarter storing each partition in a separate tablespace:

CREATE TABLE sales_by_quarter


(product_id CHAR(10),
description CHAR(50),
date_of_sale DATE,
price NUMBER(5,2),
quantity NUMBER 4)
STORAGE (INITIAL 100K NEXT 100K)
PARTITION BY RANGE (date_of_sale)
(PARTITION sales_qtr1_part VALUES LESS THAN ('01-APR-98')
TABLESPACE sales_qtr1,
PARTITION sales_qtr2-part VALUES LESS THAN ('01-JUL-98')
TABLESPACE sales_qtr2,
PARTITION sales_qtr3_part VALUES LESS THAN ('01-OCT-98')
TABLESPACE sales_qtr3,
PARTITION sales_qtr4_part VALUES LESS THAN ('01-JAN-99')
TABLESPACE sales_qtr4);

This table defines which partition will hold rows inserted into the table based on the
date_of_sale value.

New data dictionary views are available for partitions:

DBA_PART_TABLES
DBA_PART_INDEXES
DBA_PART_KEY_COLUMNS
DBA_PART_COL_STATISTICS
DBA_PART_HISTOGRAMS

_______________________________________________________________________
Prepared by M.A.Asad Page 1 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

NOTE: The partitioning option must be installed for use. This can be verified in
V$OPTION. The cost-based optimizer must be used and tables analyzed with
statistics regularly. Rule-based optimizer does not support partitions.

Performance Advantages

The cost-based optimizer can transparently eliminate partitions which do not need to be
accessed for a given query.

Using the above example, a query which is only searching for data in the first quarter can
eliminate the other three partitions from consideration. The process of partition
elimination is transparent to the end-user and can improve run times considerably.

When each partition is stored in a separate tablespace, I/O bottlenecks can be reduced by
spreading the various partitions across multiple disks.

Maintenance Advantages

Since partitions can be maintained individually, there will be a reduction in time when
data is unavailable to users, for both scheduled and unscheduled maintenance. New
commands for partition maintenance as well as enhancements to utilities such as
export/import and SQL*Loader allow for individual partitions to be manipulated while
others remain available for access.

New partition maintenance operations include:

- Add table partition


- Modify partition
- Move table partition
- Rename partition
- Drop partition
- Truncate partition
- Split partition

By allowing export and import of individual partitions, the DBA may rebuild or load less
frequently used partitions, while heavily accessed partitions remain available to users.
Tables which grow rapidly can become fragmented over time. Partitioning allows
separate storage characteristics as needed and allows defragmentation via export/import
on individual partitions, minimizing the impact of such operations on the end users.

Database Recovery Benefits

Recovery due to hardware or software failures is a part of life for the DBA. A carefully
planned and tested backup strategy is vital to prevent data loss from such occurrences.
Even with a comprehensive backup strategy, the time for recovery of large databases
poses additional problems for the DBA. The Oracle8 partitioning option addresses this
problem by allowing individual partitions to be recovered, provided the partitions are
stored in separate tablespaces.

_______________________________________________________________________
Prepared by M.A.Asad Page 2 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
By reducing the size of the segment to be recovered, the actual recovery time is reduced
and disk resources required for recovery are also minimized, which improves access to
the available partitions.

To contrast the impact of recovery of a table in Oracle7 and Oracle8, consider the
following example. Assume a table contains 50 gigabytes of historical data for General
Ledger month end closing indexed by fiscal period and calendar month. The most recent
months are accessed much more frequently than older months and are considered critical
to the user application. Suppose a disk crash occurs during month-end closing, and
queries against this table begin retuning ORA-01578 "ORACLE data block corrupted"
when selecting rows with data from 3 months ago. Some analysis has determined that
the past two months' data are not corrupted. There are important users reports which
need to be run today comparing the current month to last month, however there are also
batch jobs which must run tonight which access the entire table.

Even though the DBA has planned carefully for such a situation with daily backups and
exports to prevent data loss, the time to recover or rebuild the entire table is unacceptable
to the users.

In Oracle7, the only option is to restore and recover or to rebuild the table and import the
data from a previous export. In Oracle8 using the table partition option, the corrupted
partitions can be rebuilt while the rest of the data is available to users.

This example is an excellent case for partitioning. The DBA would define partition
ranges based on the date or fiscal period. Each partition is assigned a separate
tablespace. The individual partitions can be exported, imported, loaded, or recovered
without affecting other partitions.

To Partition or Not to Partition?

In identifying tables which would benefit from partitioning, consider the following:

1. Choose very large tables which grow rapidly, become fragmented quickly, and
present maintenance challenges which could be alleviated by separate partition
maintenance
2. Tables which have new data loaded regularly but are static thereafter
3. Summary tables, historical tables used in Decision Support Systems
4. Tables with data which has a logical partition column (date, code, type, etc)

The effort in partitioning rests with the DBA to create, maintain, backup, and regularly
analyze partition table data for use by the cost-based optimizer. A thorough
understanding of the concepts of partition tables is needed to develop the best
implementation plan. Refer to the Oracle8 Server Concepts manual for a complete
discussion of partition tables.

Improved database administration as several maintenance operations can now be done at


a partition level rather than at a table or index level. Moreover, the independence
between partitions allows the maintenance on those very partitions to take place
simultaneously.

_______________________________________________________________________
Prepared by M.A.Asad Page 3 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Database unavailability reduced. Finer grained recovery as it can be done at the partition
level.
Better distribution of data as it is possible to spread it out on several partitions and
different disks. Performance enhancement in the dispatching of queries, particularly for
data warehousing or decision making. The optimizer is capable of selecting a limited
quantity of rows associated with one or several partitions specified in the query.

This functionality is available for both indexes and tables, but cannot be implemented in
clusters.

The RULE-based optimizer does not take the partitioning of tables and indexes into
account.

Limitations.

1. The maximum number of disk fragments in a partitioned object is 64k-1. A


fragment being either a partition or a sub-partition. Therefore, you can have a
maximum number of 65535 partitions or sub-partitions per table. With Oracle
8.0.x tables could only have partitions. With Oracle 8.1.x and the introduction of
composite-partitioned tables, tables can by partition by range and then sub-
partitioned by hashed component.

2. The (64k-1) is the limit the code places on the number of fragments as we use a
two byte field to store number of partitions. Practically, 1,000 partition tables
have been tested. When a query is compiled about a table, we load metadata
information into the metadata cache (in SGA) and also into the cursor. So, while
64k-1 is the theoretical limit, there may be physical limitations well before that.

3. You cannot MOVE an entire partitioned table (either heap or index organized).
You must move individual partitions or sub-partitions.

4. If a partition is a hash partition, the only attribute you can specify in this clause is
TABLESPACE.

5. You cannot move a partition of a composite-partitioned table. You must move


each sub-partition separately with the move sub-partition clause.

6. You cannot specify this clause (MOVE PARTITION) for a partition containing
sub-partitions. However, you can move sub-partitions using the move sub-
partition clause

Some limitations at Table level:

 Clustered tables cannot be partitioned.


 A LONG or LONG RAW column in a table prevents it from being partitioned.
BLOB-type columns, on the other hand, may be partitioned
 Index Only Tables (IOT) can only be RANGE partitioned.

Some limitations at Index level:

_______________________________________________________________________
Prepared by M.A.Asad Page 4 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
 Indexes for clusters cannot be partitioned.
 Indexes for clustered tables cannot be partitioned.
 Bitmap indexes must be locally partitioned indexes.
 Bitmap indexes associated to non-partitioned tables cannot be partitioned.
 Non-prefixed global indexes are not supported.
 Local non-prefixed unique indexes must have a partitioning key included in the
index key.

7. Multi-column partition keys are limited to 16.

8. The following types of columns cannot be part of a partition key:

 LEVEL or ROWID pseudo-types.


 Columns of the following types:
1. Nested table
2. Varray
3. Object type
4. Ref
5. Rowid

 Different types of LOB (BLOB, CLOB, NCLOB, BFILE).

a. Data type Restrictions


Partitioned tables cannot have any columns with LONG or LONG RAW datatypes, LOB
data types (BLOB, CLOB, NCLOB, or BFILE), or object types. Oracle expect to support
partitioning of tables that contain LOBs in 8.1.

b. Clusters cannot be partitioned

c. Bitmap Restrictions
The only restriction is that bitmap indexes must be local to the partitioned table-they
cannot be global indexes

d. Optimizer Restrictions
Oracle8 COST BASED Optimizer supports partitions. Rule Base Optimizer is not
"partition aware" and will not support partition elimination. So any application still using
RBO will not gain any performance benefits from using partitioned tables or indexes;
they can, however, make use of ease of administration and availability features of
partitioned tables.

Physical Restrictions
Partitioned tables cannot span multiple databases; they must be within one instance.

The use of partition-extended table names has the following restrictions:

A. A partition-extended table name cannot refer to a remote schema object.

A partition-extended table name cannot contain a dblink or a synonym, which translates


to a table with a dblink. If you need to use remote partitions, you can create a view at the

_______________________________________________________________________
Prepared by M.A.Asad Page 5 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
remote site, which uses the partition-extended table name syntax and refer to that remote
view.

B. The partition-extended table name syntax is not supported by PL/SQL.

A SQL statement using the partition-extended table name syntax cannot be used in a
PL/SQL block, though it can be used through dynamic SQL via the DBMS_SQL
package. Again, if you need to refer to a partition within a PL/SQL block you can instead
use views which in turn use the partition-extended table name syntax.

C. Only base tables are allowed.

A partition extension must be specified with a base table. No synonyms, views, or any
other schema objects are allowed.

Types

Range Partitioning

Range partitioning maps rows to partitions based on ranges of column values. It is


widely used with historical data, and is very useful when dealing with data that has
logical ranges into which it can be distributed: months of the year, for example.
Performance is best when the data evenly distributes across these ranges. If partitioning
by range causes partitions to vary dramatically in size because of unequal distribution,
you may want to consider one of the other methods of partitioning

When creating range partitions, you must specify:

 Partitioning method: range


 Partitioning column(s)
 Partition descriptions identifying partition bounds

Example:

CREATE TABLE sales


( invoice_no NUMBER,
sale_year INT NOT NULL,sale_month INT NOT NULL,sale_day INT NOT
NULL )
PARTITION BY RANGE (sale_year, sale_month, sale_day)
( PARTITION sales_q1 VALUES LESS THAN (1999, 04, 01)
TABLESPACE tsa,
PARTITION sales_q2 VALUES LESS THAN (1999, 07, 01)
TABLESPACE tsb,
PARTITION sales_q3 VALUES LESS THAN (1999, 10, 01)
TABLESPACE tsc,
PARTITION sales_q4 VALUES LESS THAN (2000, 01, 01)
TABLESPACE tsd );

Hash Partitioning

_______________________________________________________________________
Prepared by M.A.Asad Page 6 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Hash partitioning is used when data cannot easily be classified into ranges but
partitioning is still required to improve performance, particularly when a database has a
few tables that contain most of the data.

Hash partitioning provides a method of evenly distributing data across a specified


number of partitions. Rows are mapped into partitions based on a hash value of the
partitioning key. Creating and using hash partitions gives you a highly tunable method of
data placement, because you can influence both availability and performance by
spreading these evenly sized partitions across I/O devices.

To create hash partitions you specify the following:

Partitioning method: hash


Partitioning columns(s)
Number of partitions or individual partition descriptions

Example:
CREATE TABLE scubagear
(id NUMBER,
name VARCHAR2 (60))
PARTITION BY HASH (id)
PARTITIONS 4
STORE IN (gear1, gear2, gear3, gear4);
Composite Partitioning

Composite partitioning partitions data using the range method, and within each partition,
sub-partitions it using the hash method. Composite partitions are ideal for both historical
data and striping. Composite Partitioning also provides improved manageability of range
partitioning and data placement, as well as the parallelism advantages of hash
partitioning.

When creating composite partitions, you specify the following:

Partitioning method: range


Partitioning column(s)
Partition descriptions identifying partition bounds
Sub-partitioning method: hash
Sub-partitioning column(s)
Number of sub-partition per partition or descriptions of sub-partition

Example:

CREATE TABLE scubagear (equipno NUMBER, equipname VARCHAR(32), price


NUMBER)
PARTITION BY RANGE (equipno) SUB-PARTITION BY HASH(equipname)
SUB-PARTITION 8 STORE IN (ts1, ts3, ts5, ts7)
(PARTITION p1 VALUES LESS THAN (1000),
PARTITION p2 VALUES LESS THAN (2000),
PARTITION p3 VALUES LESS THAN (MAXVALUE));

_______________________________________________________________________
Prepared by M.A.Asad Page 7 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Each sub-partition of a composite-partitioned table is stored in its own segment. The
partitions of a composite-partitioned table are logical structures only as their data is
stored in the segments of their sub-partition. As with partitions, these sub-partitions share
the same logical attributes. Unlike range partitions in a range partitioned table, the sub-
partition cannot have different physical attributes from the owning partition except for
the Tablespace attribute.

Composite Partitions Indexes

Composite partitioning method supports both Local and global indexes. The following
statement creates a local index on the EMP table where the index segments will be
spread across tablespaces TS7, TS8, and TS9.

CREATE INDEX emp_ix ON emp(deptno)


LOCAL STORE IN (ts7, ts8, ts9);

This local index will be equi-partitioned with the base table as follows:

It will consist of as many partitions as the base partitioned table.

Each index partition will consist of as many sub-partitions as the corresponding base
table partition.

Index entries for rows in a given sub-partition of the base table will be stored in the
corresponding sub-partition of the index.

Performance Considerations for Hash Partitioning

Hash partitions are mostly used when we do not know beforehand how much data will
map into a given range and sizes of range partitions would differ quite substantially.
Hash partitioning is an effective means of distributing data, because Oracle hashes the
data into a number of partitions, each of which can reside on a separate device. Thus,
data is evenly spread over as many devices as required to maximize I/O throughput.

The number of partitions should be a power of two (2, 4, 8, and so on) to obtain the most
even data distribution. Local indexes on hash partitions are equipartitioned with the table
data, enabling hash partitioning to be more effective in parallel index access and PDML.
Additionally, Oracle recommends hash partitioning on high cardinality key columns,
preferably unique keys.

Hash partitioning can enable partition-wise joins when two tables are hash-partitioned on
the join key. This should be the primary performance consideration for choosing a hash-
partitioning key.

Disadvantages:

-Partition pruning is limited to using equality or IN-list predicates.


-Hash partitioning is not an effective way to manage historical data.

_______________________________________________________________________
Prepared by M.A.Asad Page 8 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Performance Considerations for Composite Partitioning

Composite partitioning offers the benefits of both range and hash partitioning. With
composite partitioning, Oracle first partitions by range, and then within each range
Oracle creates sub-partition and distributes data within them using a hashing function.

Data placed in composite partitions is logically ordered only in terms of the partition
boundaries used to define the range level partitions. Consequently tables and local
indexes partitioned using the composite method support both historical data at the
partition level and the use of sub-partition as units of parallelism for parallel operations
such as PDML. Moreover, Composite partitions are eligible for partition pruning and
partition-wise joins on the range and hash dimensions.

Boosting Performance by Hash and Composite Partitions

Hash & composite partitioning were introduced in Oracle 8i in addition to the old Oracle
8.0 Range partitioning method. The three methods of partitioning can be used according
to business requirements and performance considerations. This article will explain these
considerations and help us determine when and how to partition.

Partitioning is widely used in datawarehouses and VLDB's in general. In most of the


cases large databases include a few numbers of very large tables and indexes, which
originates the need to split these objects into smaller pieces called partitions. These
partitions will have the same logical attributes of the partitioned object but they might
have their own physical attributes and can be stored in different tablespaces.

Any table or index can be partitioned into smaller storage objects called partitions. A
partition can also be partitioned into smaller pieces called sub-partition.

Partitioning columns (or sub-partitioning columns) of a table or index consist of an


ordered list of columns whose values determine how the data is partitioned or sub-
partitioned. This list can include up to 16 columns but cannot include any of the
following types of columns:

ROWID pseudocolumn
Column of the ROWID datatype
Nested table, VARRAY, object type, or REF column
LOB column (BLOB, CLOB, NCLOB, or BFILE datatype)

A row's partitioning key is an ordered list of its values for the partitioning columns.
Similarly, in composite partitioning a row's sub-partitioning key is an ordered list of its
values for the sub-partitioning columns. Oracle applies either the range or hash method
to each row's partitioning key or sub-partitioning key to determine which partition or
sub-partition the row belongs in.

When To Use Composite Partitioning


Composite Partitioning method for both tables and local indexes is used when one or
more of the following conditions apply:

_______________________________________________________________________
Prepared by M.A.Asad Page 9 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

1. Partitions must have a logical meaning and implementation to efficiently support


historical data and the contents of one partition is to be spread evenly across
multiple tablespaces and devices.
2. To enable both partition-wise joins and partition pruning for all kinds of
predicates in the same operation.
3. To use Parallel operations effectively, especially when the required degree of
parallelism is greater than the number of partitions.

Partition Pruning-----------------

Only Cost Based Optimizer is available for partitions. Rule based optimizer is not aware
of Partition Pruning nor Partition-wise joins. The plan table has 3 extra columns for
partitioning: Partition_start, Partition_stop and Partition_id. Statistics must be gathered
by dbms_stat package or Analyze command; statistics can also be gathered on the
partition and sub-partition levels.

The following example shows how to recognize partition pruning in the execution plan
for all partitioning types:

Range Partitions

Table emp_range is partitioned by range on hiredate, assume that the tables emp and dept
from a standard Oracle schema exist.

CREATE TABLE emp_range


PARTITION BY RANGE(hiredate)
(
PARTITION emp_p1 VALUES LESS THAN (TO_DATE('1-JAN-1991','DD-MON-
YYYY')),
PARTITION emp_p2 VALUES LESS THAN (TO_DATE('1-JAN-1993','DD-MON-
YYYY')),
PARTITION emp_p3 VALUES LESS THAN (TO_DATE('1-JAN-1995','DD-MON-
YYYY')),
PARTITION emp_p4 VALUES LESS THAN (TO_DATE('1-JAN-1997','DD-MON-
YYYY')),
PARTITION emp_p5 VALUES LESS THAN (TO_DATE('1-JAN-1999','DD-MON-
YYYY'))
)
AS SELECT * FROM emp;

Example A

EXPLAIN PLAN FOR SELECT * FROM emp_range;

Operation | Name | Rows | Bytes| Cost | Pstart | Pstop

SELECT STATEMENT | |105 | 8K| 1| |

_______________________________________________________________________
Prepared by M.A.Asad Page 10 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
PARTITION RANGE ALL | | | | | 1 | 5
TABLE ACCESS FULL |EMP_RANGE |105 | 8K| 1| 1 | 5

In example A, the partition iterator covers all partitions (option ALL), because a
predicate was not used for pruning. The PARTITION_START and PARTITION_STOP
columns of the plan table show access to all partitions from 1 to 5.

Example B

EXPLAIN PLAN FOR SELECT * FROM emp_range


WHERE hiredate >= TO_DATE('1-JAN-1995','DD-MON-YYYY');

Operation | Name | Rows | Bytes| Cost | Pstart| Pstop


SELECT STATEMENT | | 3 | 54 | 1| |
PARTITION RANGE ITERATOR | | | | | 4| 5
TABLE ACCESS FULL |EMP_RANGE| 3 | 54 | 1| 4| 5

In example B, the partition row source iterates from partition 4 to 5, because we prune
the other partitions using a predicate on hiredate.

Hash Partitions

Oracle displays the same information for hash partitioned objects, except that the
partition row source name is PARTITION HASH instead of PARTITION RANGE.
Remember that with hash partitioning, pruning is only possible using equality or IN-list
predicates.

Composite Partitions

Table emp_comp that is range partitioned on hiredate and sub-partitioned by hash on


deptno.

CREATE TABLE emp_comp PARTITION BY RANGE(hiredate) SUB-PARTITION BY


HASH(deptno)
SUB-PARTITION 3
(
PARTITION emp_p1 VALUES LESS THAN (TO_DATE('1-JAN-1991','DD-MON-
YYYY')),
PARTITION emp_p2 VALUES LESS THAN (TO_DATE('1-JAN-1993','DD-MON-
YYYY')),
PARTITION emp_p3 VALUES LESS THAN (TO_DATE('1-JAN-1995','DD-MON-
YYYY')),
PARTITION emp_p4 VALUES LESS THAN (TO_DATE('1-JAN-1997','DD-MON-
YYYY')),
PARTITION emp_p5 VALUES LESS THAN (TO_DATE('1-JAN-1999','DD-MON-
YYYY'))
) AS SELECT * FROM emp;

_______________________________________________________________________
Prepared by M.A.Asad Page 11 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Example A

EXPLAIN PLAN FOR SELECT * FROM emp_comp;

Operation | Name | Rows | Bytes | Cost| Pstart | Pstop


SELECT STATEMENT | | 105 | 8K | 1 | |
PARTITION RANGE ALL| | | | | 1 | 5
PARTITION HASH ALL| | | | | 1 | 3
TABLE ACCESS FULL|EMP_COMP| 105 | 8K | 1 | 1 | 15

Example A shows the plan when Oracle accesses all sub-partition of all partitions of a
composite object. Two partition row sources are used for that purpose: a range partition
row source to iterate over the partitions and a hash partition row source to iterate over the
sub-partition of each accessed partition.

In this example, the range partition row source iterates from partition 1 to 5. Within each
partition, the hash partition row source iterates over sub-partition 1 to 3 of the current
partition. As a result, the table access row source accesses sub-partition 1 to 15.

Example B

EXPLAIN PLAN FOR SELECT * FROM emp_comp


WHERE hiredate = TO_DATE('15-FEB-1997', 'DD-MON-YYYY');

Operation | Name | Rows | Bytes| Cost | Pstart| Pstop


SELECT STATEMENT | | 1 | 96 | 1 | |
PARTITION HASH ALL | | | | | 1| 3
TABLE ACCESS FULL |EMP_COMP | 1 | 96 | 1 | 13 | 15

In example B, only the last partition, partition 5, is accessed. This partition is known at
compile time, so we do not need to show it in the plan. The hash partition row source
shows accessing of all sub-partition within that partition; that is, sub-partition 1 to 3,
which translates into sub-partition 13 to 15 of the emp_comp table.

Example C

EXPLAIN PLAN FOR SELECT * FROM emp_comp WHERE deptno = 20;

Operation | Name | Rows | Bytes| Cost | Pstart| Pstop

SELECT STATEMENT | | 2 | 200 | 1| |


PARTITION RANGE ALL| | | | | 1 | 5
TABLE ACCESS FULL |EMP_COMP| 2 | 200 | 1| |

_______________________________________________________________________
Prepared by M.A.Asad Page 12 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

In this example, the predicate deptno = 20 enables pruning on the hash dimension within
each partition, so Oracle only needs to access a single sub-partition. The number of that
sub-partition is known at compile time, so the hash partition row source is not needed.

Partition-wise Joins

1. Partial Partition-wise Joins

The following example shows emp_range joined on the partitioning column and is
parallelized. This enables use of partial partition-wise join, because the dept table is not
partitioned. Oracle dynamically partitions the dept table before the join.

ALTER TABLE emp PARALLEL 2;

ALTER TABLE dept PARALLEL 2;

EXPLAIN PLAN FOR SELECT /*+ ORDERED USE_HASH(D) */ ename, dname


FROM emp_range e, dept d
WHERE e.deptno = d.deptno
AND e.hiredate > TO_DATE('29-JUN-1996','DD-MON-YYYY');

Operation | Name | Rows | Bytes| Cost | TQ |IN-OUT| PQ Distrib |Pstart|


Pstop
SELECT STATEMENT | | 1 | 51 | 3| | | |
HASH JOIN | | 1 | 51 | 3 | 2,02 | P->S |QC (RANDOM) |
PARTITION RANGE ITERATOR| | | | | 2,02 | PCWP | | 4| 5
TABLE ACCESS FULL|EMP_RANGE| 3 | 87 | 1 | 2,00 | PCWP | | 4|
5
TABLE ACCESS FULL |DEPT | 21 | 462 | 1 | 2,01 | P->P |PART (KEY) |

The plan shows that the optimizer selects partition-wise join, because the DIST column
contains the text PART (KEY), or partition key.

2. Full Partition-wise Joins

In the following example, emp_comp and dept_hash are joined on their hash partitioning
columns.This enables use of full partition-wise join. You can easily recognize Full
partition-wise joins in the plan as The PARTITION HASH row source appears on top of
the join row source in the plan table output.

CREATE TABLE dept_hash

PARTITION BY HASH(deptno)
PARTITIONS 3
PARALLEL
AS SELECT * FROM dept;

_______________________________________________________________________
Prepared by M.A.Asad Page 13 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

To show the plan for the query, enter:

EXPLAIN PLAN FOR SELECT /*+ ORDERED USE_HASH(D) */ ename, dname

FROM emp_comp e, dept_hash d


WHERE e.deptno = d.deptno

AND e.hiredate > TO_DATE('29-JUN-1996','DD-MON-YYYY');

Operation | Name| Rows | Bytes| Cost| TQ |IN-OUT| PQ Distrib |Pstart| Pstop


SELECT STATEMENT | | 2 | 102| 2 | | | | |
PARTITION HASH ALL | | | | | 4,00| PCWP | | 1| 3
HASH JOIN | | 2 | 102 | 2 | 4,00| P->S | QC (RANDOM)|
PARTITION RANGE ITERATOR | | | | 4,00| PCWP | | 4| 5
TABLE ACCESS FULL |EMP_COMP | 3 | 87 | 1 | 4,00| PCWP | | 10|
15
TABLE ACCESS FULL |DEPT_HASH| 63 | 1K | 1 | 4,00| PCWP | | 1|
3

In Oracle 8.1, the ability to partition a table with an object type, tables that contain
collection types, tables with REF data types, and tables with LOB data types is
available. The steps to partition a table with user-defined datatypes and LOB datatypes
is very similar to partitioning a relational table. The same partitioning methods of Range,
Hash, and Composite can be applied to partitioning relational tables as well as object
tables. This bulletin will go through how to partition a table with a user-defined
datatype or LOB datatype and what to look out for.

Column Objects
A column object is an object that appears only in table columns or as attributes of other
objects. In the following example, an object type called shipping_address is created.
The object type shipping_address consists of customer information. A table called
packing_list is created with a column called shipping with a datatype of
shipping_address. The table is partitioned using the range partition method. The
partition key is made up of product_id and shipping.state. Since the column shipping is
to be included in the partition key its scalar attribute, state, must be used. When a table
column has an object datatype you need to take the scalar attribute of that column if you
want to use it for partitioning.

For example:

CREATE TYPE shipping_address AS OBJECT


( customer_name VARCHAR2(40),
address VARCHAR2(20),
city VARCHAR2(20),
state CHAR(2),
zip NUMBER)
/

CREATE TABLE packing_list

_______________________________________________________________________
Prepared by M.A.Asad Page 14 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
( product_id NUMBER,
product_name VARCHAR2(20),
shipping SHIPPING_ADDRESS)
PARTITION BY RANGE (product_id, shipping.state)
( PARTITION partition_1 VALUES LESS THAN (100, 'CO')
TABLESPACE ts1
STORAGE (INITIAL 10m NEXT 15m),
PARTITION partition_2 VALUES LESS THAN (300, 'NY'),
PARTITION partition_3 VALUES LESS THAN (400, 'WA'))
/

To insert into a table that contains a column with an object datatype certain steps are
required. Since the column shipping has an object datatype, the constructor for the object
type shipping_address must be used when inserting values into it.

INSERT INTO packing_list VALUES


(250, 'Computer', shipping_address('Angie', '1234 Weeping Willow',
'Rockledge', 'FL',32935))
/

In order to view the data in the table you can just issue a select on the table. In order to
select individual attributes from the shipping column, PL/SQL needs to be used instead
of SQL.

Row Objects
A row object is a table that is made up of an object type. A row object can function as if
it were a relational table. Since a row object is based off an object, the object type needs
to be created first. Next, to create the row object table, the syntax CREATE TABLE
tablename OF objectname is used. This syntax will create a table based off an object
type. When partitioning a row object, the partition declaration is specified immediately
after the create statement.

CREATE TYPE Books AS OBJECT


(Book_Name VARCHAR2(20),
Author VARCHAR2(20),
ISBN NUMBER)
/

CREATE TABLE Lib_Books OF Books


PARTITION BY HASH(ISBN)
(PARTITION Lib_Books_P1
TABLESPACE ts1,
PARTITION Lib_Books_P2
TABLESPACE ts2)
/

DML and DDL can be performed on a row object table in the same manner as a
relational table with a few exceptions. Adding a column to a row object is not possible
since the table is made up of an object type. Adding a row to a row object can be

_______________________________________________________________________
Prepared by M.A.Asad Page 15 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
accomplished in two ways. A row can be added in the same manner as a relational table
or by using the object type constructor.

INSERT INTO Lib_Books VALUES('Partitions','Brian Garrett', 9834);


Or
INSERT INTO Lib_Books VALUES(Books('Adv Partition','John Smith',2349));

Selecting from a row object table can be accomplished as if it were a relational table.

REF
A REF encapsulates a reference to a row object of a specified object type. When a
relational table has a column with a REF datatype the REF will store the Object ID
(OID) of a particular row from a row object table. Since a REF references a row object,
the object type must first be created followed by the row object table.

CREATE TYPE Books AS OBJECT


(Book_Name VARCHAR2(20),
Author VARCHAR2(20),
ISBN NUMBER)
/

CREATE TABLE Lib_Books OF BOOKS


PARTITION BY HASH(ISBN)
(PARTITION Lib_Books_P1
TABLESPACE ts1,
PARTITION Lib_Books_P2
TABLESPACE ts2)
/

INSERT INTO Lib_Books VALUES('Partitions','Brian Garrett', 9834);

Once the row object table is created then the tables that will reference it can be created.
The table Lib_Card has a column called Checked_Books which has a REF datatype that
references the object type Books.

CREATE TABLE Lib_Card


(First_Name varchar2(20),
Last_Name varchar2(20),
Checked_Books REF Books)
PARTITION BY RANGE(Last_Name)
(PARTITION Lib_Card_P1 VALUES LESS THAN ('Cook'),
PARTITION Lib_Card_P2 VALUES LESS THAN ('Miller'),
PARTITION Lib_Card_P3 VALUES LESS THAN (MAXVALUE))
/

When using a REF datatype in a partitioned table, the REF datatype cannot be part of the
partition key. The Checked_Books column will contain an Object ID, which cannot be
partitioned on, to where the row exists. To insert a value into the Lib_Card table we need

_______________________________________________________________________
Prepared by M.A.Asad Page 16 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
to select a particular row from a row object, Lib_Books. This will place the Object ID
for the row in the Checked_Books column.

INSERT INTO Lib_Card


SELECT 'Angie', 'Garrett', ref(a)
FROM Lib_Books a
WHERE Book_Name = 'Partitions'
/

When you select on a column with a REF datatype you will received the Object ID of the
row and not the data. In order to view the actual data within a columnwith a REF
datatype you need to use the DEREF function. The following is an example of using the
DEREF function.

SELECT First_Name, Last_Name, DEREF(Checked_Books)


FROM Lib_Card
/

Collections (VARRAY and Nested Tables)


A collection type describes data units made up of the same datatypes. Collections are
stored in a separate table from the base table where the column with a collection type is
declared. First, create an object type called Books. Next, a nested table type based off
the object type Books is created.

CREATE TYPE Books AS OBJECT


(Book_Name VARCHAR2(20),
Author VARCHAR2(20),
ISBN NUMBER)
/

CREATE TYPE Books_NT AS TABLE OF Books


/

In the table Lib_Card, the column Checked_Books has a nested table datatype,
Books_NT. The physical nested table is created next as Books_NT_TAB along with its
storage parameter. When the partition declaration is specified, the column with the
nested table datatype cannot be a part of the partition key. The actual data for the column
Checked_Books is stored in a nested table, Books_NT_TAB, which is stored separately
from the partitioned table and is not partitioned. The column with the nested table
datatype will point to the column in the nested table.

Create table Lib_Card


(First_Name varchar2(20),
Last_Name varchar2(20),
Card_No number,
Checked_Books Books_NT)
NESTED TABLE Checked_Books STORE AS Books_NT_TAB
STORAGE(INITIAL 10M NEXT 15M)
PARTITION BY RANGE(Card_No)
(PARTITION Lib_Card_P1 VALUES LESS THAN (450),

_______________________________________________________________________
Prepared by M.A.Asad Page 17 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
PARTITION Lib_Card_P2 VALUES LESS THAN (625),
PARTITION Lib_Card_P3 VALUES LESS THAN (MAXVALUE))
/

To insert data into the Lib_Card table the nested table type, Books_NT constructor must
be used. Also within the Books_NT constructor the Books' object constructor must be
used. More than one value can be entered into a column with the nested table datatype.

INSERT INTO Lib_Card VALUES


('Jim', 'Smith', 536,
Books_NT(
Books('Into to Oracle', 'May Bee', 23456),
Books('Scuba Diving', 'Melvin Myers', 65432)))
/

Selecting from the Lib_Card table can be done by simply selecting the columns from the
table. In order to select individual attributes from the nested table column PL/SQL is
necessary.

LOB Datatypes
LOB datatypes provide the ability to store large amounts of structured or non-structured
data. LOB datatypes can hold up to four gigabytes of data. When a table is partitioned
that contains a LOB datatype, the LOB can be stored in a separate location. The LOB
will be equi-partitioned with the partitioned table. In the following example there is a
table called Library that contains two columns called Book_Description and
Book_Audio with CLOB and BFILE datatypes respectively.

CREATE TABLE Library


( Card_Holder VARCHAR2(20),
Book_Name VARCHAR2(20),
Author VARCHAR2(20),
Book_Number NUMBER,
Book_Description CLOB,
Book_Audio BFILE)
LOB (Book_Description) STORE AS (STORAGE (INITIAL 2m NEXT 5m))
PARTITION BY HASH(Book_Number)
(PARTITION Library_P1
TABLESPACE ts1
LOB (Book_Description) STORE AS (TABLESPACE t3),
PARTITION Library_P2
TABLESPACE ts2
LOB (Book_Description) STORE AS (TABLESPACE t1))
/

The storage parameters for LOBs can be stored in either the table declaration or the
partition declaration. Since Hash partition method is being used no other storage
parameters besides tablespace location can be provided in the partition declaration. The
Library_P1 partition will be stored in the ts1 tablespace while the LOB,
Book_Description, will be stored in tablespace t3. Since Book_Audio is a BFILE
datatype a "BFILE locator" is stored in the partitioned table which points to the physical

_______________________________________________________________________
Prepared by M.A.Asad Page 18 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
file that is stored at the operating system. The "BFILE locator" stores the location of the
file along with other controlling information. To insert into a table that contains LOB
datatypes the following syntax can be used.

INSERT INTO Library VALUES


('Erin Robinson', 'Intro to Oracle', 'John Doe', 34567,
'An introduction to how Oracle works',
BFILENAME('Audio_Dir', 'Int_Oracle.aud'))
/

The BFILENAME function uses two parameters: The Directory name and the BFILE
name. Before inserting into the BFILE, the directory where the file resides must already
exists on the system. Using the CREATE DIRECTORY syntax, the directory where the
file resides can be recorded in the Oracle Dictionary with the following syntax:

CREATE DIRECTORY Audio_Dir as '<directory name complete path>';

In order to view the data within a partitioned table that contains columns with LOB
datatypes, you can simply select the columns you would like to view in sqlplus with the
exception of BFILE datatypes. The DBMS_LOB package must be used to do any
manipulation to a BFILE datatype.

The following is an example of selecting from the Library table:

SELECT Card_Holder, Book_Name, Author, Book_Number, Book_Description


FROM Library;

Partition Pruning based on Joins to Partitioning Criteria Stored in Dimension Table

In certain queries a partitioned table may rely on a join predicate to eliminate partitions.
One method of achieving this is to use a nested loops join driving from the non-
partitioned (dimension) table and then eliminating partitions based on the rows returned.
However, with large data volumes, this may perform sub optimally.
In 8.1.6 the code was enhanced to allow elimination to occur by recursively determining
which partitions to eliminate using a recursive lookup independent of the join method
used. This may open up more efficient retrieval paths.

How it works

The non-partitioned (dimension) table that contains the information to eliminate


partitions is compared with the dictionary information regarding partition ranges in the
partitioned table. This is used to generate a list of partitions to be accessed.

This activity only occurs if certain cost and selectivity criteria are met. Although a join
between the partition key and the dimension table needs to be present, the actual
elimination IS NOT done in the join step itself; rather it is performed as recursive SQL.
Therefore elimination is not joining order dependant.

Consider the following partitioned table:

_______________________________________________________________________
Prepared by M.A.Asad Page 19 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
create table range_part (col1 number(9))
partition by range (col1)
(partition p1 values less than (10),
partition p2 values less than (20),
partition p3 values less than (30),
partition p4 values less than (MAXVALUE));

Behavior prior to Oracle8i Release 8.1.6:

Prior to 8.1.6., Join based Partition elimination could only occur using a nested loops
join:
EXPLAIN PLAN FOR
SELECT col1
FROM dept d, range_part p
WHERE d.deptno = p.col1
AND d.loc = 'DALLAS';

Typically this would generate the following plan:

Plan Table
------------------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
------------------------------------------------------------------------------------------
| SELECT STATEMENT | | 3 | 102 | 2| | |
| NESTED LOOPS | | 3 | 102 | 2| | |
| TABLE ACCESS FULL |DEPT | 1 | 21 | 1| | |
| PARTITION RANGE ITERATOR| | | | | KEY | KEY |
| TABLE ACCESS FULL |RANGE_PART | 4 | 52 | 1 | KEY | KEY |
------------------------------------------------------------------------------------------

This plan shows that partitions from the range_part partition are eliminated based on
rows retrieved from the join with the dimension table dept. This type of elimination can
only be done with a Nested loops join and the dimension table must be accessed prior at
the partition in order to eliminate partitions.

Enhanced functionality (post Oracle8i release 8.1.6)

Partition elimination can also be achieved without requiring any specific join method or
join order (subject to criteria outlined below).

alter session set "_subquery_pruning_cost_factor"=1; -- overrides cost restrictions


alter session set "_subquery_pruning_reduction"=100; -- overrides selectivity
restrictions

explain plan for


select /*+ ordered use_hash(p) */ col1
from range_part p, dept d
where d.deptno = p.col1
and d.loc = 'DALLAS';

_______________________________________________________________________
Prepared by M.A.Asad Page 20 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Plan Table
------------------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
------------------------------------------------------------------------------------------
| SELECT STATEMENT | | 3 | 102 | 3| | |
| HASH JOIN | | 3 | 102 | 3| | |
| PARTITION RANGE ITERATOR| | | | | KEY | KEY |
| TABLE ACCESS FULL |RANGE_PART | 328 | 4K| 1 | KEY | KEY |
| TABLE ACCESS FULL |DEPT | 1 | 21 | 1| | |
------------------------------------------------------------------------------------------

Note:

1. This plan was forced using 2 parameters to override the cost and selectivity
criteria mentioned above. The relationship between these parameters and the
costing algorithms is complex and is beyond the scope of this article.
2. The hash join hint is included simply to prevent the cheaper (in this instance)
Nested Loops join from occurring and is simply for illustration purposes.
3. Similarly the ordered hint an join order change has been added to illustrate that
the pruning information is not coming from the join to the dept table, but from a
recursive step within the PARTITION RANGE ITERATOR step
4. The join method is irrelevant. Pruning will also occur using a sort merge join

Criteria for choosing to prune

The optimizer makes the decision on whether to choose this type of pruning based on:

 Parameter _subquery_pruning_enabled = true to validate this mechanism.


 The presence of a join on the partition key column of the fact table.
 The cost and selectivity of one of the dimension tables being less than 2
predefined values. The cost of the recursive subquery must not exceed 5% of the
cost of accessing all data in the partitioned fact table and the predicates on the
dimension table must select less than 50% of the data in the dimension table.

As long as there is a valid join on the partition key then pruning can be forced by
setting :

_subquery_pruning_cost_factor =1

and

_subquery_pruning_reduction =100.

This effectively overrides the default choice made by the optimizer.

There is no way to directly force pruning to occur using hints.

How it Works

_______________________________________________________________________
Prepared by M.A.Asad Page 21 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
The PARTITION RANGE ITERATOR step in the explain plan is where this
functionality takes place. The dimension table is used to determine a list of partitions that
should be accessed using the dictionary information. In a recursive session, a lookup
against the dimension table (dept in this example) is performed. The column values that
join to the Partition key of table range_part are looked up against the dictionary
information recording which key values reside in which partitions. This information is
gathered using an internal function and returns a distinct list of partitions to be examined.
The other partitions are then pruned. This forms one half of the join operation.

NB the lookup of the dimension table, at this stage, is not shown in the plan as it is
recursive SQL, just as selects against OBJ$ (for instance) are not shown in the plan, even
though they must occur. The recursive SQL can be viewed, as detailed below.

The other half of the join is independant of this operation; partition pruning does not use
the join itself to prune the partitions.

Displaying the recursive SQL

Plan Table 'Other' column

The recursive SQL that is generated under the PARTITION RANGE ITERATOR step
can be displayed by selecting the 'other' column from the plan_table after 'explaining the
query. This new features is enabled starting with 9i version only.
set long 2000

SELECT other
FROM plan_table
WHERE operation like 'PARTITION%'
and options = 'ITERATOR'
and partition_start = 'KEY'
and partition_stop = 'KEY';
Output:
OTHER (Manually formatted)
---------------------------------------------------------------
SELECT distinct TBL$OR$IDX$PART$NUM("RANGE_PART", 0, d#, p#, "COL1")
FROM (SELECT "D"."DEPTNO" "COL1"
FROM "DEPT" "D"
WHERE "D"."LOC"='DALLAS')
ORDER BY 1
The TBL$OR$IDX$PART$NUM function is responsible for retrieving details of which
partition a particular value resides in.

Oracle 8i New Feature: Composite-Partitioned Table Exchange Enhancement

Overview and Background

Oracle 8 introduced Partitioned Tables, a new feature to replace the interim Partition
View enhancement of Oracle7. The purpose of Partitioned Tables is to provide
performance and maintainability features suitable for very large database environments,
particularly data warehouses. Essentially, a partitioned table is a physical construct that
permits a logical database table to be divided into separate physical entities that can each

_______________________________________________________________________
Prepared by M.A.Asad Page 22 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
reside in its own tablespace. This feature can be thought of as a new logical layer in the
Oracle database architecture, permitting a table to span multiple tablespaces.

There are three types of partitioned tables in Oracle 8i: Range, Hash and Composite.
Range partitioning requires that rows identified by a "partition key" fall into predefined
ranges of value. That is, the value range into which its partition key column falls
determines the partition to which a row belongs.

A hash-partitioned table's rows have their physical location determined by applying a


hash value to the partition key column, providing for deterministic but well distributed
physical data spread.

A composite-partitioned table has a two-part partition key, the leftmost being a range-
partition key and the rightmost being a hash partition key. composite-partitioned tables
make use of sub-partition for their hashed components. A composite-partitioned table
therefore consists of a set of range-partitions, which are each composed of a set of
hashed-partitions.

The most obvious performance advantage of partitioning is that the Cost Based
Optimizer (CBO) can optimize a query to ignore entire partitions that are known not to
contain rows for a given query. That is, if the partition is organized appropriately by
range of values, CBO can dynamically eliminate inapplicable partitions from a query,
limiting physical block I/O and reducing query time.

For example, if you need to query sales data for the most recent three fiscal months, it
would be advantageous to physically group the rows by fiscal month. If you could then
restrict your scanning to blocks within segments containing rows for your fiscal months,
you could eliminate lots of I/O. A range-partitioned table can ensure that all rows for a
common fiscal period are physically located together, allowing CBO to ignore those
partitions with inapplicable rows.

Additionally, there are significant maintenance advantages with partitioned tables for
large scale databases, particularly those that store and query data by a moving window of
range value, such as fiscal or calendar period. In those cases, new data is typically loaded
at the end of a period and rows from an older period are removed. In order to minimize
downtime associated with inserting and deleting rows, Oracle provides for the exchange
of a single partition with an otherwise identical non-partitioned table without interrupting
access to the other partitions. This exchange occurs within the metadata layer of Oracle
and is fast because no actual movement of table data is required.

Without physically moving rows, Oracle essentially swaps a table with a partition,
turning the table into a partition. The result is that the row contents of the table are now
contained within the partition and the row contents of the partition are now in the table.

Of course, there are rules and restrictions that apply, such that the table must construct
identically to its corresponding partition. After the exchange, the resulting table, which
was previously an outdated partition, can now be archived or dropped as appropriate.

For detailed information on partitioned table design and associated implementation


requirements and restrictions, refer to the Oracle 8i Concepts manual.

_______________________________________________________________________
Prepared by M.A.Asad Page 23 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Oracle 8i New Feature

Prior to 8i, it was only possible to affect an exchange between a range-partitioned table
and a non-partitioned table. This new feature extends the exchange functionality to
include exchange between a range partition (including its hashed-sub-partition)
belonging to a composite-partitioned table and a separate hash-partitioned table. This
feature is especially suited to data warehouse applications that make use of a "sliding
window" approach to managing historical data, enabling full use of composite-
partitioned tables in such a strategy.

Since the rule still prevails that a table partition can only be exchanged with a table that
is structured identically to the partition, it stands to reason that the structure of the hash-
partitioned table must be identical in structure to the hash sub-partition of the composite-
partitioned table. Moreover, the column values that will be associated with the range
partition key of the composite-partitioned table must fall within the defined range of that
partition.

This new feature is an extension of existing functionality and does not affect existing
partitioned tables or syntax.

Syntactically, a composite-partitioned table T1 is exchanged with a hash-partitioned table


T2 in this manner:

ALTER TABLE T1 EXCHANGE PARTITION P1 WITH TABLE T2 WITH


VALIDATION;

Example

To demonstrate this feature, we will define two partitioned tables, a composite-


partitioned table and a corresponding hash-partitioned table. Of course, this is a trivial
example that does not make use of physical storage options, but it will serve our purpose
of demonstration. First, the composite table:

SQL> create table t1 (i number, j number)


2 partition by range(i)
3 sub-partition by hash(j)
4 (partition p1 values less than (11)
5 (sub-partition t1_p1s1,
6 sub-partition t1_p1s2)
7 ,
8 partition p2 values less than (21)
9 (sub-partition t1_p2s1,
10 sub-partition t1_p2s2)
11 ,
12 partition p3 values less than (31)
13 (sub-partition t1_p3s1,
14 sub-partition t1_p3s2)
15 ,

_______________________________________________________________________
Prepared by M.A.Asad Page 24 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
16 partition p4 values less than (MAXVALUE)
17 (sub-partition t1_p4s1,
18 sub-partition t1_p4s2)
19 );

Table created.

Next, the hash table:

SQL>
SQL> create table t2 (i number, j number)
2 partition by hash(j)
3 (partition p1,
4 partition p2);

Table created.

Note the similarity between the partitions of t2 and the sub-partition of t1. The number of
partitions in t2 is equal to the number of sub-partition in each partition of t1. There is no
"values less than" syntax for t2 since the hash function on column "j" will determine to
which partition a row belongs.

Now, let's populate the hash-partitioned table:

SQL>
SQL> begin
2 for i in 1 .. 10 loop
3 insert into t2 values (i, i*4);
4 end loop;
5 end;
6 /

PL/SQL procedure successfully completed.

Note that the PL/SQL block we used to populate the hash-partitioned table limits the
values of column i to values that fall within the range of partition P1 of our composite
table. This demonstrates the importance of data preparation. Values of columns "i" will
become important when the table becomes a partition of table t1 and must fall within the
appropriate range. In this case, t2 will become partition p1 of table t1, so values of "i" in
table t2 must be less than 11.

Let's peek at the data to make sure it is "clean" and fits our partition requirements. First,
we will make sure t1 is empty:

SQL>
SQL> select * from t1;

no rows selected

t1 is empty, as expected. Here is t2, the one we just populated:

_______________________________________________________________________
Prepared by M.A.Asad Page 25 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

SQL>
SQL> select * from t2;

I J
---------- ----------
2 8
5 20
6 24
9 36
10 40
1 4
3 12
4 16
7 28
8 32

10 rows selected.

Since all rows in column I are less than 11, our hash-partitioned table looks like partition
P1 of the composite table t1. And, since column "i" is identical in type for both tables
and column "j" is identical in type for both tables, and column "j" is indeed a hashed
partition key for both tables, table t2 is said to be "equipartitioned" with partition P1 of
table t1.

To finish our demonstration, let's swap the hashed-partition table with partition P1 of the
composite table:

ALTER TABLE t1 EXCHANGE PARTITION P1 WITH TABLE t2 WITH


VALIDATION;

The VALIDATION option will force the partition key data type composition and the
range values of column "i" to be validated, so that if one or more rows do not qualify for
the partition range for P1 an error is reported and the exchange does not occur:

ORA-14099: all rows in table do not qualify for specified partition

Just to be meticulous, let's verify that the swap really occurred:

SQL> select * from t2;

no rows selected

SQL> select * from t1;

I J
--------- ---------
2 8
5 20
6 24

_______________________________________________________________________
Prepared by M.A.Asad Page 26 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
9 36
10 40
1 4
3 12
4 16
7 28
8 32

10 rows selected.

Good news! No rows in t2, t1 was empty before and now looks just like t2 used to look.

Metadata Magic

When we first created the tables, this is what user_objects looked like:

SQL> select substr(object_name, 1, 10), substr(subobject_name, 1, 10),


2 object_type, object_id, data_object_id, to_char(last_ddl_time, 'HH:MI:SS') from
3 user_objects order by object_id
4 /

SUBSTR(OBJ SUBSTR(SUB OBJECT_TYPE OBJECT_ID DATA_OBJECT_ID


TO_CHAR(
---------- ---------- ------------------ --------- -------------- --------
T1 TABLE 21909 02:13:59
T1 P1 TABLE PARTITION 21910 02:13:59
T1 P2 TABLE PARTITION 21911 02:13:59
T1 P3 TABLE PARTITION 21912 02:13:59
T1 P4 TABLE PARTITION 21913 02:13:59
T1 T1_P1S1 TABLE SUB-PARTITION 21914 21914 02:13:59
T1 T1_P1S2 TABLE SUB-PARTITION 21915 21915 02:13:59
T1 T1_P2S1 TABLE SUB-PARTITION 21916 21916 02:13:59
T1 T1_P2S2 TABLE SUB-PARTITION 21917 21917 02:13:59
T1 T1_P3S1 TABLE SUB-PARTITION 21918 21918 02:13:59
T1 T1_P3S2 TABLE SUB-PARTITION 21919 21919 02:13:59
T1 T1_P4S1 TABLE SUB-PARTITION 21920 21920 02:13:59
T1 T1_P4S2 TABLE SUB-PARTITION 21921 21921 02:13:59
T2 TABLE 21922 02:14:00
T2 P1 TABLE PARTITION 21923 21923 02:14:00
T2 P2 TABLE PARTITION 21924 21924 02:14:00

16 rows selected.

After the exchange, the same query produces this:

SUBSTR(OBJ SUBSTR(SUB OBJECT_TYPE OBJECT_ID DATA_OBJECT_ID


TO_CHAR(
---------- ---------- ------------------ --------- -------------- --------

_______________________________________________________________________
Prepared by M.A.Asad Page 27 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
T1 TABLE 21909 02:15:50
T1 P1 TABLE PARTITION 21910 02:13:59
T1 P2 TABLE PARTITION 21911 02:13:59
T1 P3 TABLE PARTITION 21912 02:13:59
T1 P4 TABLE PARTITION 21913 02:13:59
T1 T1_P1S1 TABLE SUB-PARTITION 21914 21923 02:15:50
T1 T1_P1S2 TABLE SUB-PARTITION 21915 21924 02:15:50
T1 T1_P2S1 TABLE SUB-PARTITION 21916 21916 02:13:59
T1 T1_P2S2 TABLE SUB-PARTITION 21917 21917 02:13:59
T1 T1_P3S1 TABLE SUB-PARTITION 21918 21918 02:13:59
T1 T1_P3S2 TABLE SUB-PARTITION 21919 21919 02:13:59
T1 T1_P4S1 TABLE SUB-PARTITION 21920 21920 02:13:59
T1 T1_P4S2 TABLE SUB-PARTITION 21921 21921 02:13:59
T2 TABLE 21922 02:15:50
T2 P1 TABLE PARTITION 21923 21914 02:15:50
T2 P2 TABLE PARTITION 21924 21915 02:15:50

16 rows selected.

Notice that the OBJECT_ID column for the objects all stayed the same, but the
DATA_OBJECT_ID was exchanged between the sub-partition T1_P1S1, T1_P1S2 of
table t1 and partitions P1, P2 of table t2. In addition, the LAST_DDL_TIME column of
the objects will be updated for the table object and the affected sub-partition of t1 and the
table and partitions of t2.

Summary

While syntactically simple, the new ability of Oracle 8i to permit exchange of hashed
sub-partition of composite-partitioned tables with hashed-partitioned tables is powerful.
It extends functionality to significantly enhance business operations for large scale
environments, increasing flexibility of database design while improving performance and
availability for mission critical database operations.

Partitioning enhancements in Oracle8i

List of Oracle8i Enhancements Specific to Partitioning.

- Hash Partitioning
- Composite Partitioning
- Partitioned Index-Organized tables & Secondary indexes defined on IOT
- MERGE operation to allow merging of range partitions
- Updatable partition keys
- Partitioned LOB and object tables
- Partition-wise join

Hash Partitioning
Hash partitioning stripes the data into the designated number of partitions by using a
hash function on the partitioning columns. It allows data thatdoes not easily lend itself to

_______________________________________________________________________
Prepared by M.A.Asad Page 28 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
range partitioning to be easily partitionedfor performance reasons and provides more
control over physical placementof data.

SQL> CREATE TABLE emp_hpart(


2> empno NUMBER(4) NOT NULL,
3> ename VARCHAR2(10),
4> sal NUMBER(7,2))
5> STORAGE (INITIAL 50k NEXT 50k)
6> PARTITION BY HASH(empno) PARTITIONS 4
7> STORE IN (data01,data02,data03,data04)
8> /

This example will create a table split into 4 system defined physical partitions, named
SYS_Px where x is a number from a sequence, spread across four tablespaces.

SQL> SELECT table_name,partitioning_type,partition_count


2> FROM dba_part_tables;
3> WHERE table_name='EMP_HPART';

TABLE_NAME PARTITI PARTITION_COUNT


------------------------------ ------- ---------------
EMP_HPART HASH 4

SQL> SELECT table_name, partition_name, tablespace_name


2> FROM dba_tab_partitions
3> WHERE table_name='EMP_HPART';

TABLE_NAME PARTITION_NAME TABLESPACE_NAME


-------------- ------------------ ------------------
EMP_HPART SYS_P9 DATA01
EMP_HPART SYS_P10 DATA02
EMP_HPART SYS_P11 DATA03
EMP_HPART SYS_P12 DATA04

The syntax can be changed in order to allow for explicit partition naming and positioning
as follows, in this case the number of PARTITIONS is omitted as it is induced from the
subsequent partition declaration...:

SQL> CREATE TABLE emp_hpart


....
5> PARTITION BY HASH(empno)
6> (PARTITION P1 TABLESPACE data01,
7> PARTITION P2 TABLESPACE data02,
8> PARTITION P3 TABLESPACE data03,
9> PARTITION P4 TABLESPACE data04)
10> /

As an example consider an EMP_HPART table consisting of 8000 records, each partition


above would contain approximately 2000 records. Due to the nature of hash partitioning
we are limited in the amount of partition maintenance we can carry out. We are unable to

_______________________________________________________________________
Prepared by M.A.Asad Page 29 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SPLIT, DROP, MERGE as is now possible to do with range partitioning, attempts to do
so will result in...:

ORA-14255: table is not partitioned by Range or Composite Range method

We can however ADD partitions, this works by splitting each partition in turn as and
when a new partition is added...:

ALTER TABLE emp_hpart ADD PARTITION p13 TABLESPACE data01;

The kernel would rehash the rows that are contained SYS_P9 between SYS_P9 and the
new partition, P13, i.e. Approximately 1000 rows each. If we then added another
partition, P14, the kernel would rehash the rows contained in SYS_P10. Adding
partitions in such a manner would result in an uneven distribution of the data hence it is
recommended that the number of partitions should always be a power of 2 (2, 4,8,16 and
so on).

Composite partitioning
Composite partitioning is, as you'd expect a combination of range partitioning and hash
partitioning. This method of partition could be used in a situation where a table in a data
warehouse is loaded once quarterly (suggesting a partition by range), but each quarters
data would amount to inordinately large partitions.

Composite partitioning is achieved by first partitioning by range, with each partition then
further sub-partitioned by a hash function. This method provides all the benefits of range
partitioning with added control over data manageability as provided by hash partitioning.

SQL> ALTER SESSION SET NLS_DATE_FORMAT='DD-MON-YYYY';


Session altered.
SQL> CREATE TABLE sales99_cpart (
2> sale_id NUMBER NOT NULL,
3> sale_date DATE,
4> prod_id NUMBER,
5> qty NUMBER)
6> PARTITION BY RANGE(sale_date)
7> SUB-PARTITION BY HASH(prod_id) SUB-PARTITION 4
8> STORE IN (data01,data02,data03,data04)
9> (PARTITION cp1 VALUES LESS THAN('01-APR-1999'),
10> PARTITION cp2 VALUES LESS THAN('01-JUL-1999'),
11> PARTITION cp3 VALUES LESS THAN('01-OCT-1999'),
12> PARTITION cp4 VALUES LESS THAN('01-JAN-2000'))
13> /

In this example the SALES99_CPART table is range partitioned on SALE_DATE into


four separate ranges representing quarters. Each of these ranges is then sub-partitioned
into 4 hash partitions, hashed by PROD_ID, resulting 16 physical segments. Querying
DBA_SEGMENTS shows this...:

SQL> column segment_name format a15


SQL> column partition_name format a15

_______________________________________________________________________
Prepared by M.A.Asad Page 30 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL> column tablespace_name format a15
SQL> SELECT segment_name, partition_name, segment_type, tablespace_name
2> FROM dba_segments
3> WHERE segment_name='SALES99_CPART';

SEGMENT_NAME PARTITION_NAME SEGMENT_TYPE


TABLESPACE_NAME
--------------- --------------- ------------------ ---------------
SALES99_CPART SYS_SUBP21 TABLE SUB-PARTITION DATA01
SALES99_CPART SYS_SUBP22 TABLE SUB-PARTITION DATA02
SALES99_CPART SYS_SUBP23 TABLE SUB-PARTITION DATA03
...
...
SALES99_CPART SYS_SUBP31 TABLE SUB-PARTITION DATA03
SALES99_CPART SYS_SUBP32 TABLE SUB-PARTITION DATA04
SALES99_CPART SYS_SUBP33 TABLE SUB-PARTITION DATA01
SALES99_CPART SYS_SUBP34 TABLE SUB-PARTITION DATA02
SALES99_CPART SYS_SUBP35 TABLE SUB-PARTITION DATA03
SALES99_CPART SYS_SUBP36 TABLE SUB-PARTITION DATA04

There are also some new data dictionary views to accomodate these new structures...:

DBA_TAB_SUB-PARTITION
DBA_SUBPART_KEY_COLUMNS
DBA_IND_SUB-PARTITION

Modified views...:

DBA_TAB_PARTITIONS
DBA_PART_TABLES
DBA_PART_INDEXES
DBA_OBJECTS

Composite partitioned indexes are always local and are stored in the same tablespace as
the table sub-partition by default. However, it is possible to specify tablespaces at either
the index level, or at the index sub-partition level.

Range partitioned global indexes are supported on composite partitioned tables but
composite partitioned global indexes are not.

Partitioned Index-Organized tables (IOT's)


Index-organized tables store data in B*Tree structures similar to that of ordinary indexes
on ordinary tables, with the option to store non-key columns in a user-defined overflow
tablespace.
They can help to reduce overall storage requirements as there is no need for a separate
index structure duplicating columns already defined in the table. They can also provide
faster key-based access to table data since the index values and all non-index columns
are potentially stored in the same segment, using fewer I/O operations to retrieve a row.

_______________________________________________________________________
Prepared by M.A.Asad Page 31 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
In Oracle8i, the ability to partition index-organized tables comes with a few points of
note...:

 Only Range partitioning is suppported.


 Partition columns must be a subset of primary key columns.
 Secondary indexes may also be partitioned, both locally and globally.
 OVERFLOW data segments are always equipartitioned with the table partitions.
 Unlike nonpartitioned IOT's, a partitioned IOT cannot contain LOB or Object
type columns.

MERGE operation
The ALTER TABLE MERGE PARTITIONS command now makes it possible for
adjacent range partitions to be merged into a single larger partition with an inherited
upper boundary that is the higher of the two merged partitions. Prior to Oracle8i it was
only possible to do this by extracting the data to be merged into a dummy table and then
exchanging it back into a new partition.

Note:
 New tablespace and storage clauses may be specified on the INTO PARTITION
clause, otherwise the table defaults are used.
 Data physically gets moved to a new data segment.
 MERGE partition is not supported for hash partitioning, use COALESCE instead.

Updatable partition keys


When ROW MOVEMENT is enabled users have the ability to update partitioning key
columns in such a way that a row no longer belongs in its current partition, causing such
rows to migrate to the appropriate partition.

Prior to Oracle8i, and when ROW MOVEMENT is disabled, this sort of updates are
disallowed, resulting in an error returned to the user. For example...:

SQL> UPDATE sales_99 SET sale_date='04-APR-99' WHERE sale_date='05-MAY-99';


UPDATE sales_99 SET sale_date='04-APR-99' WHERE sale_date='05-MAY-99'
*
ERROR at line 1:
ORA-14402: updating partition key column would cause a partition change
SQL> ALTER TABLE sales_99 ENABLE ROW MOVEMENT;
Table altered.
SQL> UPDATE sales_99 SET sale_date='04-APR-99' WHERE sale_date='05-MAY-99';
4 rows updated.
Note:
 This clause may only be specified for partitioned tables.
 ROW MOVEMENT is disabled by default.
 Overridden by specifying ENABLE ROW MOVEMENT as part of
ALTER/CREATE TABLE statements.
 The status of this for specific tables is externalised via the ROW_MOVEMENT
column of ALL/DBA/USER_TABLES views.

_______________________________________________________________________
Prepared by M.A.Asad Page 32 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Partitioned Object and LOB tables
The ability to create tables with user defined datatypes and LOB datatypes was available
in Oracle8 but the ability to partition these type of tables was only made available in
Oracle8i.
The theory is exactly the same as ordinary relational tables, ie. you can partition by
Range, by Hash, or by Composite methods. The only differences are the extra
considerations of LOB storage and such like.

Partition-wise Joins
There are several different variations of Partition-wise Joins and are best described in
'Oracle8i Tuning' manual. In summary, partition-wise joins can reduce query response
times in two ways; by reducing the amount of data exchanged between query servers
when joins execute in parallel, and by reducing the amount of memory required by serial
joins. In OPS environments it also helps limit the data traffic between nodes.

Secondary indexes on partitioned tables


This is pretty self-explanatory really, prior to Oracle8i it was not possible to have a
secondary index on any other columns in a partitioned table.

Oracle9i Partitioning Enhancements, LIST Partitioning.


ORACLE9i LIST PARTITIONING

Oracle9i adds a new partitioning model called LIST partitioning to the set of partition
methods already being supported in the Oracle RDBMS. List partitioning enables the
user to explicitly control how rows map to partitions. You do this by specifying a list of
discrete values for the partitioning key in the description for each partition. This is
different from range partitioning, where a range of values is associated with a partition
and from hash partitioning, where a hash function controls the row-to-partition mapping.
The advantage of list partitioning is that you can group and organize unordered and
unrelated sets of data in a natural way.

Either Tables or Indexes may be partitioned with LIST method.

To support list partitioning, changes have been made to the various command syntax.
The examples below will illustrate these changes.

Partition pruning, Partition Wise Join and parallelism are also usable with this method.

The following restrictions concern the LIST partitioning:

1. Only one single column can be specified as key partition.


2. The IOTs cannot be partitioned with LIST method.
3. The same values cannot be specified in different or same partitions.
4. The MAXVALUE bound used with RANGE partitioning cannot be used with
LIST method.
5. A partition cannot be empty.

New In 9.2.0

_______________________________________________________________________
Prepared by M.A.Asad Page 33 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
In Oracle version 9.2.0 you can also specify a default partition into which rows that do
not map to any other partition are mapped.

Example:

CREATE TABLE sales_list


(salesman_id NUMBER(5),
salesman_name VARCHAR2(30),
sales_state VARCHAR2(20),
sales_amount NUMBER(10),
sales_date DATE)
PARTITION BY LIST(sales_state)
(
PARTITION sales_west VALUES('California', 'Hawaii'),
PARTITION sales_east VALUES ('New York', 'Virginia', 'Florida'),
PARTITION sales_central VALUES('Texas', 'Illinois')
PARTITION sales_other VALUES(DEFAULT)
);

In the above example inserting value with sales_state 'COLORADO' would map to
Sales_other partition.

The DEFAULT partition enables you to avoid specifying all possible values for a list-
partitioned table by using a default partition, so that all rows that do not map to any other
partition do not generate an error.

I. List Partition Maintenance

On Table:

1. CREATE a List Partitioned Table.


2. MODIFY (ADD/DROP Values).
3. ADD Partition.
4. MERGE.
5. RENAME.
6. SPLIT.
7. TRUNCATE.
8. EXCHANGE.
9. MOVE.
10. MODIFY DEFAULT ATTRIBUTES.
11. MODIFY REAL ATTRIBUTES.

On Index:

The List Partitioning is only usable on local indexes which are equi-partitioned with
underlying tables. The set of commands usable on these indexes is limited because it
depends directly from the table's administration.

The only commands which are executable on it are:

_______________________________________________________________________
Prepared by M.A.Asad Page 34 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
1. MODIFY DEFAULT ATTRIBUTES.
2. MODIFY REAL ATTRIBUTES.
3. REBUILD PARTITION.
4. RENAME PARTITION.

II. List Partition Pruning ( Partition Elimination )

There are three pruning types supported for LIST partitioned tables:

1. EQUALITY ( col1='AB' )
2. IN-LIST ( col1 IN ('AB','AC') )
3. RANGE ( col1 <= 'AB' )

Range List Partitioning - Oracle 9.2 Enhancement

Range List Partitioning

This is a new feature introduced in Oracle 9.2 . This is a complex partition method
created by joining two existing methods - Range partitioning (introduced with 8.0) and
List partitioning (introduced in 9.0.1).

The Range-List method partitions the data using Range method and then the sub-
partitioning is done using the List method. As evident, unlike the composite Range-Hash
method, we can control precisely in which sub-partition each row would go. But there
cannot be multiple sub-partition keys as the list method doesn't support it.

This type of partitioning is supported only on Heap tables.

An example of Range-List partition creation is:

CREATE TABLE empdata


( empno number,
ename varchar2(20),
deptno number,
continent varchar2(6),
hiredate date,
job varchar2(10),
salary number)
PARTITION BY RANGE (deptno)
SUB-PARTITION BY LIST (continent)
(PARTITION d1_emp VALUES LESS THAN (100)
(SUB-PARTITION d1_con1 VALUES ('ASIA', 'AUST'),
SUB-PARTITION d1_con2 VALUES ('AMER'),
SUB-PARTITION d1_con3 VALUES ('AFRICA'),
SUB-PARTITION d1_con4 VALUES ('EUROPE')),
PARTITION d2_emp VALUES LESS THAN (maxvalue)
(SUB-PARTITION d2_con1 VALUES ('ASIA', 'AUST'),
SUB-PARTITION d2_con2 VALUES ('AMER'),
SUB-PARTITION d2_con3 VALUES ('AFRICA'),

_______________________________________________________________________
Prepared by M.A.Asad Page 35 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SUB-PARTITION d2_con4 VALUES ('EUROPE'))
);

As with the Range-Hash method, you aren't obliged to specify all the different sub-
partitions at any levels. You can use the SUB-PARTITION TEMPLATE clause likes
below:

CREATE TABLE empdata


( empno number,
ename varchar2(20),
deptno number,
continent varchar2(6),
hiredate date,
job varchar2(10),
salary number)
PARTITION BY RANGE (deptno)
SUB-PARTITION BY LIST (continent)
SUB-PARTITION TEMPLATE
(SUB-PARTITION d1_con1 VALUES ('ASIA', 'AUST') tablespace PART1,
SUB-PARTITION d1_con2 VALUES ('AMER') tablespace PART2,
SUB-PARTITION d1_con3 VALUES ('AFRICA') tablespace PART3,
SUB-PARTITION d1_con4 VALUES ('EUROPE') tablespace PART3
)
(PARTITION d1_emp VALUES LESS THAN (100),
PARTITION d2_emp VALUES LESS THAN (maxvalue)
);

All the storage clauses are inherited from the partition level, except the TABLESPACE
clause. If not defined at the partition level, they are inherited from the underlying table.

Sample data to load:


INSERT INTO empdata(empno,deptno,continent) values(1,50,'AMER');
--> Will be inserted into sub-partition d1_con2
INSERT INTO empdata(empno,deptno,continent) values(2,100,'ASIA');
--> Will be inserted into sub-partition d2_con1
INSERT INTO empdata(empno,deptno,continent) values(3,150,'EUROPE');
--> Will be inserted into sub-partition d2_con4
INSERT INTO empdata(empno,deptno,continent) values(4,50,'AUST');
--> Will be inserted into sub-partition d1_con1
INSERT INTO empdata(empno,deptno,continent) values(5,250,'AFRICA');
--> Will be inserted into sub-partition d2_con3

Partitioning Enhancements in Oracle 10g


1. Partitioned Index-Organized Tables (IOTs) Enhancements

a. List-partitioned IOTs

SQL> CREATE TABLE sales (


acct_no NUMBER(5),

_______________________________________________________________________
Prepared by M.A.Asad Page 36 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
acct_name CHAR(30),
amount_of_sale NUMBER(6),
week_no INTEGER,
sale_details VARCHAR2(1000),
PRIMARY KEY (acct_no, acct_name, week_no))
ORGANIZATION INDEX
INCLUDING week_no
OVERFLOW tablespace overflow_here
PARTITION BY LIST (week_no) (
PARTITION part1234 VALUES (1, 2, 3, 4) tablespace ts1,
PARTITION part5678 VALUES (5, 6, 7, 8) tablespace ts1,
PARTITION partdefault VALUES (DEFAULT) tablespace ts1);

SQL> select index_name, partition_name, status from user_ind_partitions


where index_name = 'SYS_IOT_TOP_52216';

INDEX_NAME PARTITION_NAME STATUS


------------------------------ ------------------------------ --------
SYS_IOT_TOP_52216 PART1234 USABLE
SYS_IOT_TOP_52216 PART5678 USABLE
SYS_IOT_TOP_52216 PARTDEFAULT USABLE

All maintenance operations allowed on list-partitioned tables are now supported for
index-organized tables.

b. Global index maintenance for partitioned IOTs

SQL> alter table sales truncate partition PART1234;


Table truncated.

SQL> select index_name, partition_name, status from user_ind_partitions


where index_name = 'SYS_IOT_TOP_52216';

INDEX_NAME PARTITION_NAME STATUS


------------------------------ ------------------------------ --------
SYS_IOT_TOP_52216 PART1234 USABLE
SYS_IOT_TOP_52216 PART5678 USABLE
SYS_IOT_TOP_52216 PARTDEFAULT USABLE

Prior to Oracle 10g, the global indexes on partitioned IOTs were not maintained when
partition maintenance operations were performed. After a DROP, TRUNCATE, or
EXCHANGE PARTITION, the global indexes became UNUSABLE. Other partition
maintenance operations such as MOVE, SPLIT, or MERGE PARTITION did not make
the global indexes UNUSABLE, but the performance of global index-based access was
degraded because the guess-DBAs (database block addresses) stored in the index rows
were invalidated.
Global index maintenance now prevents these issues from happening, keeps the index
usable, and maintains the guess-DBAs.

c. Local partitioned bitmap indexes on partitioned IOTs

_______________________________________________________________________
Prepared by M.A.Asad Page 37 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

SQL> CREATE TABLE piot_test (


empno NUMBER(4),
ename VARCHAR2(10),
edept NUMBER(4),
constraint emp_iot_pk primary key(empno))
ORGANIZATION INDEX
MAPPING TABLE
PARTITION BY RANGE(empno) (
partition emp_p100 VALUES LESS THAN (50) ,
partition emp_p200 VALUES LESS THAN (MAXVALUE) );

Table created.

SQL> create bitmap index i_b_edept on piot_test(edept) local;


Index created.

The concept of a mapping table is extended to support a mapping table that is


equipartitioned with respect to the base table. This enables the creation of bitmap indexes
on partitioned IOTs.

d. LOB columns are now supported in all types of partitioned IOTs.

2. Local Partitioned Index Enhancements

Prior to Oracle 10G, when a partitioned table is modified using DDL commands such as
ADD, SPLIT, MERGE, or MOVE, the associated local index partitions are placed in the
default tablespace, or in the same tablespace as the data segments. In addition, for most
of the partition maintenance commands the newly created local index segments are left
in an UNUSABLE state. These segments need to be rebuilt in order to avoid potential
ORA-1502 errors (index is in direct load state).

With Oracle Database 10g, when a partitioned table is modified, the new UPDATE
INDEXES clause can be used to specify storage attributes of the corresponding local
index segments as well as automatically rebuild them. If the clause is used without
specifying any indexes, the indexes are located according to the existing rules but are
rebuilt to make them USABLE. The example below shows the effects of the UPDATE
INDEXES clause. The example assumes that all the partitions are initially stored in the
USERS tablespace and that they all contain data.

Example :

SQL> ALTER TABLE sales_test


MOVE PARTITION s_q3 TABLESPACE USERS2
UPDATE INDEXES
(saled_test_ix
(PARTITION s_q3 TABLESPACE EXAMPLE));

_______________________________________________________________________
Prepared by M.A.Asad Page 38 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL> SELECT index_name, tablespace_name, status
FROM user_ind_partitions
WHERE index_name like 'SALES%';

INDEX_NAME TABLESPACE_NAME STATUS


------------------- --------------- ------
SALES_TEST_IX_S_Q1 USERS USABLE
SALES_TEST_IX_S_Q2 USERS USABLE
SALES_TEST_IX_S_Q3 EXAMPLE USABLE

Note: This feature reduces the level of management required and increases the
availability of the data in the table. When the index is marked as UNUSABLE, any query
or DML command using the partitioned table returns an ORA-1502 error unless you
explicitly ask to skip the unusable indexes.

The SKIP_UNUSABLE_INDEXES session parameter is now a dynamic initialization


parameter and its default value are TRUE at all levels. By changing the default setting
for the SKIP_UNUSABLE_INDEXES to TRUE, the optimizer ignores UNUSABLE
indexes at parse time and avoids potential ORA-1502 errors.

However, when SKIP_UNUSABLE_INDEXES is set to TRUE, the optimizer can select


a sub optimal execution plan by avoiding UNUSABLE indexes. Since the SQL statement
generates no errors, the user is unaware that the optimizer chose a sub optimal execution
plan. For this reason, the database records an alert message in the alert log file whenever
an index is marked as unusable.

Actions

Introduction to Partition Views in Oracle 7.3


Table Partitioning is the term used to split a large table into a number of smaller, more
manageable tables, yet still allow database queries to 'see' the table as one large table
despite its distinct parts.

BENEFITS OF TABLE PARTITIONING

The benefits of this are two-fold :

1. Each individual partition becomes much more manageable. Eg,

 One partition can be loaded, deleted etc without affecting others


 Partitions can be individually backed up. This is useful for historic data.

2. The optimizer may be able to make use of the 'partitioning' information :

If the partitions are divided by month (for example) then a query on the 'whole' table
may be able to infer that it only needs to look at one table rather than visit all the
partitioned tables.

Prior to 7.3

_______________________________________________________________________
Prepared by M.A.Asad Page 39 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Prior to 7.3 Oracle supports 'manual partitions' if the manual partition is onstructed as
follows :

 Create a number of distinct tables that have the same structure. Ie, the same
number of columns of the same name and type and created in the same order.

 Add the required indexes to EACH table. Note that the indexes must be the same
and must exist for each partitioned component.

(If indexes are not created exactly the same then these indexes will not )
(be available for the optimizer to use to access the underlying partitions)
(and for the optimizer to recognise the view as a manual partition. )

 Create a UNION-ALL view that selects from these partitions. An example is :

create view partitioned_emp as


select * from emp1
union all
select * from emp2
union all
select * from emp3;

Note that check constraints can be applied to the underlying tables (emp1, emp2, emp3)
to ensure that the relevant data is placed in the required partition. This becomes
important later when we select from the view and specify additional predicates to skip
various partitions.

 The UNION-ALL view can be amended to quickly index into the partition
required by using a small control table. Let us assume that the above view
definition is to be used and that EMP1 is for employees in department 10, EMP2
for employees in department 20 and so on.

If we create a small control table with one numeric column as follows :

create table control (dept number);


insert into control values (10);
insert into control values (20);
insert into control values (30);

Then we can reconstruct the view as :

create view partitioned_emp as


select /*+ ORDERED */ a.*
from control b, emp1 a
where b.dept = 10
union all
select /*+ ORDERED */ a.*
from control b, emp1 a
where b.dept = 20
union all

_______________________________________________________________________
Prepared by M.A.Asad Page 40 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
select /*+ ORDERED */ a.*
from control b, emp1 a
where b.dept = 30;

Now, a select such as 'select * from partitioned_emp where dept = 10' will not visit the
emp2 and emp3 partitions. This can obviously represent a major saving with a small
table and a quick method to 'index into' the required data without the overhead and space
requirements of an index.

With 7.3
Minor Changes

The terminology 'manual partition' has been replaced with 'view partitioning'. Both are
equivalent and mean the same thing but the latter will be used within any Oracle
documentation.

The explain plan output will reflect that a view partition has been recognised by
displaying the word PARTITION next to the UNION-ALL operator. If an explain plan
does not show the keyword 'PARTITION' then the view is NOT being treated as a
partition view and partitions are NOT thus eliminated from the query. This is likely to be
due to an index or column mismatch between tables in the partition view.

A new boolean init.ora parameter named 'partition_view_enabled' will be added to allow


the optimizer to skip partitions when it can. This is DISABLED by default. This can be
enabled in a session using:

alter session set partition_view_enabled=true;

Constant Predicate Transitivity

Oracle 7.3 has been improved to allow the use of constant predicate transitivity which
can be of particular benefit with manual partitions.

Briefly, this feature allows the optimizer to infer additional predicates from the query.
For example,

where c1=5 and c2=c1 and c2=7


..
becomes
..
where c1=5 and c2=c1 and c2=7 AND 5=7
~~~~~~~
This is obviously false and means that any table access using these predicates can be
skipped. When this is expanded to the UNION-ALL view :

select * from partitioned_emp where deptno = 10;

Then the optimizer knows that it can avoid access to emp2 and emp3.

_______________________________________________________________________
Prepared by M.A.Asad Page 41 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Other Changes

Both the Cost Based Optimizer and Parallel Query features have been improved
significantly and offer better support for view partition processing.

Partition Views in 7.3: Examples and Tests


Partition View Tips:
1. The Cost Based Optimizer must be used to enable partition elimination code.
2. The init.ora parameter <PARAMETER:PARTITION_VIEW_ENABLED> must
be set to true.
3. Partition elimination is enforced via check constraints or where clause predicates
within the view definition.
4. Underlying tables must have identical definitions.
5. You must specify ALL columns or select * from the partitioned tables in the view.
6. Underlying tables must have identical indexes.

Partition Elimination Examples:

Table & view set up for the examples:

Partition is a view made up of 3 tables, PART1, PART2 & PART3.

SQL> describe partition


Name Null? Type
------------------------------- -------- ----
COL1 NUMBER
COL2 VARCHAR2(10)
COL3 DATE

Table PART1:
col1 contains only values 1-10000
col3 contains only 08-NOV-96

Table PART2:
col1 contains only values 10001-20000
col3 contains only 07-DEC-96

Table PART3:
col1 contains only 20001-30000
col3 contains only 07-JAN-97

Constraint definition:
~~~~~~~~~~~~~~~~~~~~~~
The constraint on each table in this case is a date range.

alter table part1 add constraint p1_chk


check (col3 between '01-NOV-96' and '30-NOV-96');
alter table part2 add constraint p2_chk
check (col3 between '01-DEC-96' and '31-DEC-96');
alter table part3 add constraint p3_chk

_______________________________________________________________________
Prepared by M.A.Asad Page 42 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
check (col3 between '01-JAN-97' and '31-JAN-97');

create or replace view partition as


select * from part1
union all
select * from part2
union all
select * from part3
/

TEST 1: PARTITION VIEW WITH CHECK CONSTRAINT AND RULE BASED


OPTIMIZER
SQL> alter session set optimizer_goal = rule;

Note that the following select only needs to look at table PART1.

SQL> select * from partition


where col3 = '08-NOV-96'
and col1 = 10;

Explain Plan
_____________________________
SELECT STATEMENT Optimizer=RULE
VIEW OF 'PARTITION'
UNION-ALL
TABLE ACCESS (BY ROWID) OF 'PART1'
INDEX (RANGE SCAN) OF 'P1_I' (NON-UNIQUE)
TABLE ACCESS (BY ROWID) OF 'PART2'
INDEX (RANGE SCAN) OF 'P2_I' (NON-UNIQUE)
TABLE ACCESS (BY ROWID) OF 'PART3'
INDEX (RANGE SCAN) OF 'P3_I' (NON-UNIQUE)

Output from event 10046 level 8 - this demonstrates which partitions are visited - i.e. all
of them. Only 1 of the tables actually needs to be visited because in the other cases the
index lookup returned no matching rows.

WAIT #1: nam='db file sequential read' ela= 0 p1=10 p2=553 p3=1 - P1_I Index
WAIT #1: nam='db file sequential read' ela= 0 p1=10 p2=554 p3=1 - P1_I Index
WAIT #1: nam='db file sequential read' ela= 0 p1=10 p2=103 p3=1 - PART1 Table
WAIT #1: nam='db file sequential read' ela= 3 p1=10 p2=653 p3=1 - P2_I Index
WAIT #1: nam='db file sequential read' ela= 0 p1=10 p2=654 p3=1 - P2_I Index
WAIT #1: nam='db file sequential read' ela= 1 p1=10 p2=753 p3=1 - P3_I Index
WAIT #1: nam='db file sequential read' ela= 0 p1=10 p2=754 p3=1 - P3_I Index

Note:
For this query we do not have to access Tables PART2 or PART3 or their indexes,
because they do not contain data that we are interested in. The RBO is unable to
eliminate these partitions from the query and we have to check (via the index in this
case) if the data we want is in these unnecessary partitions. In this case we do not have to

_______________________________________________________________________
Prepared by M.A.Asad Page 43 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
lookup the underlying tables as all the information we require can be satisfied by reading
the index alone.

TEST 2: PARTITION VIEW WITH CHECK CONSTRAINT AND COST BASED


OPTIMIZER

SQL> alter session set optimizer_goal = choose;

SQL> select * from partition


where col3 = '08-NOV-96'
and col1 = 10;

Explain Plan
______________________________________________________
SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=1 Bytes=26)
VIEW OF 'PARTITION' (Cost=5 Card=1 Bytes=26)
UNION-ALL (PARTITION)
FILTER
TABLE ACCESS (BY ROWID) OF 'PART1' (Cost=2 Card=1 Bytes=24)
INDEX (RANGE SCAN) OF 'P1_I' (NON-UNIQUE)
FILTER
TABLE ACCESS (BY ROWID) OF 'PART2' (Cost=2 Card=1 Bytes=26)
INDEX (RANGE SCAN) OF 'P2_I' (NON-UNIQUE)
FILTER
TABLE ACCESS (BY ROWID) OF 'PART3' (Cost=2 Card=1 Bytes=26)
INDEX (RANGE SCAN) OF 'P3_I' (NON-UNIQUE)

Notice the filter operations in the explain plan. These indicate that at execution time we
can eliminate unwanted partitions.

At Execution time the raw 10046 trace file output shows:

WAIT #1: nam='db file sequential read' ela= 2 p1=10 p2=553 p3=1 - P1_I Index
WAIT #1: nam='db file sequential read' ela= 0 p1=10 p2=554 p3=1 - P1_I Index
WAIT #1: nam='db file sequential read' ela= 1 p1=10 p2=103 p3=1 - PART1 Table

With CBO we have eliminated the unnecessary partitions (PART2 & PART3) and
only select from PART1.

TEST 3: REMOVE THE INDEXES ON THE UNDERLYING PARTITIONED TABLES

If you remove the indexes from the tables you get the following:

SQL> alter session set optimizer_goal = choose;


select * from partition
where col3 = '08-NOV-96'
and col1 = 10;

Explain Plan

_______________________________________________________________________
Prepared by M.A.Asad Page 44 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SELECT STATEMENT [CHOOSE] Cost=75
VIEW PARTITION Cost=75 Card=1 Bytes=26
UNION-ALL PARTITION
FILTER
TABLE ACCESS FULL PART1 [ANALYZED] Cost=23 Card=1 Bytes=24
FILTER
TABLE ACCESS FULL PART2 [ANALYZED] Cost=26 Card=1 Bytes=26
FILTER
TABLE ACCESS FULL PART3 [ANALYZED] Cost=26 Card=1 Bytes=26

Notice that the filter lines in the explain plan are still there.

At Execution time the Raw trace output from 10046 level 8 shows:

WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=103 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 1 p1=10 p2=111 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=119 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=127 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=135 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=143 p3=8 - PART1 TABLE
WAIT #1: nam='db file sequential read' ela= 0 p1=10 p2=151 p3=1 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=52 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=60 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=68 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 1 p1=10 p2=76 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 1 p1=10 p2=84 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=92 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=100 p3=2 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 1 p1=10 p2=2 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=10 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=18 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=26 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 1 p1=10 p2=34 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=42 p3=8 - PART1 TABLE
WAIT #1: nam='db file scattered read' ela= 0 p1=10 p2=50 p3=2 - PART1 TABLE

In this case the unnecessary Partitions are eliminated, but we have to do a full table scan
(FTS) because the indexes have been removed. Note that we only FTS the PART1 table,
PART2 & PART3 have been eliminated.

V7.3: Partition Views (UNION ALL)

Here is a short description of the partition view feature in 7.3. There will be a more
detailed white-paper on partition view, but the info below explains that basics.

Introduction

Partition views are a new feature in Release 7.3 designed to provide better support for
very large tables that are commonly found in data warehousing environments.

_______________________________________________________________________
Prepared by M.A.Asad Page 45 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
The partition view feature offers two primary benefits:

- improved managability and availability of large tables


- improved performance for table scans on certain queries of these large tables

What is a partion view?

An example partition view is:


create view sales as
select * from jan_sales
union all
select * from feb_sales
...
union all
select * from dec_sales

Each of the base tables (the monthly sales tables) *must* be identical in terms of column
names, column datatypes, and indexes. These tables *must* also have a CHECK
constraint on its partitioning column (thus, the jan_sales table must have a check
constraint on the date column which constrains the date to fall between Jan 1 and Jan
31).

All of these base tables, indexes, constraints as well as the UNION-ALL view definition
are be created by the DBA.

Managability and availability benefits

A partition view greatly simplifies the administration of very large tables.

Consider the example of of a data warehousing containing a large 'sales' table. Once per
month, the DBA loads all of new sales data into this table. Thus, the DBA would need to
drop all of the indexes on the sales, load the new data, and rebuild the indexes. Since the
sales table is very large, this could be a lengthy operation. Morever, the sales table is (for
most practical DSS application) is not available while these load and index operations
are occurring.

Using the partition view feature, the DBA could load the new month's data into a
separate partition and build indexes on this new partition without impacting the original
partition view. Then, after the new partition is entirely built and indexed, the DBA could
recreate the UNION-ALL view to include the new partition. The sales partition view is
unavailable for a very short length of time ... only while the UNION-ALL view is being
built. Moreover, because the indexes are much smaller, the length of time to load and
index a new month's worth of data is dramatically decreased.

Performance benefits of partition views

Any UNION-ALL view (even in earlier releases of Oracle7) can reap the aforementioned
managability benefits; however, unless the UNION-ALL view offers reasonable query
performance, the managability benefits are useless.

_______________________________________________________________________
Prepared by M.A.Asad Page 46 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
The enhancement in Release 7.3 is to insure that the query performance on UNION-ALL
views will at least equivalent to (and, in many cases, much better than) single-table
access. Note that these performance enhancements are only effective when all of the
partitions have the appropriate CHECK constraints and when all of the partitions have
identical column definitions and indexes.

There are two basic performance enhancements for partition views:

partition elimination
parallel execution of UNION-ALL views

Certain queries may not require data from all of the partitions of a view partition. For
example, consider the following query:

select sum(revenues) from sales


where sales_date between '15-JAN-96' and '15-FEB-96';

With 7.3's new support for partition views, Oracle will evaluate the above query using
*only* the January partition and the February partition; the remaining ten partitions will
not be accessed. This feature is commonly called 'partition elimination'.

Partition elimination is only effective when querying based on the partitioning column;
in this example, the partitioning column is the sales_date column. But the performance
savings can be significant. In the previous example, the partition view feature results in
ten of the twelve partitions being eliminated from query processing. This could represent
six-fold performance gain.

An additional enhancement in 7.3 is the parallel execution of UNION-ALL views. All


queries on UNION-ALL views can be executed in parallel (when using the Parallel
Query Option). It is *very* important to note that the partitioning scheme is absolutely
independent of the degree of parallelism (this starkly contrasts with many of our
competitior's parallel query architecture, in which the physical data partitioning
determines the degree of parallelism).
Oracle will dynamically distribute the data of a UNION ALL view across all parallel
query processes, and partition elimintation will not impact the degree of parallelism.

Limitations of Partition Views

Partition views do not support DML operations. For this reason, partition views are most
appropriate for read-only applications (such as data warehouses).

Conclusion

Partition views can be very effective for handling very large tables in data warehousing
environments. The manageability of these large tables is vastly improved, with
significant performance improvements for many queries.

How to Implement Partitioning in Oracle Versions 8 and 8i

_______________________________________________________________________
Prepared by M.A.Asad Page 47 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Partitioning is available in V7 through the use of partitioned views. This type of view is
based on a set of tables that have the same structure; specifically, the columns and data
types are identical and in the same order. Moreover, for each table there must exist an
index based on the 'partitioning key'. If this is not the case, then the optimizer fails to
take the partitioned view into account when it builds its execution plan.

Then is built as a UNION ALL type view of the different tables:

Create view partitioned_emp as


select * from emp1
union all
select * from emp2
union all
select * from emp3;

A new parameter, PARTITION_VIEW_ENABLED, is available starting in version 7.3.


When it is set to TRUE, it allows the optimizer to only take into account the partitions
associated with the select predicate.

This partitioning method has certain negative aspects:

- DDL and DML orders are not taken into account.


- Export, import, and loader tools are not taken into account.
- There is no possibility to use global indexes.

I.2 Version 8:

Partitioning is offered as an option in Oracle V8 Enterprise Server Edition. It is an


option that requires specific selection during installation of the RDBMS that enables it to
be linked into the kernel.
This new option is useful in addressing performance problems and database
administration issues in Very Large Database (VLDB) environments. Administrators of
databases with historical data are particularly interested in this option. Administrators of
OLTP databases concerned the performance and/or availability also benefit with the
implementation of this option.

Advantages:

 Improved database administration as several maintenance operations can now be


done at a partition level rather than at a table or index level. Moreover, the
independence between partitions allows the maintenance on those very partitions
to take place simultaneously.
 Database unavailability reduced. Finer grained recovery as it can be done at the
partition level.
 Better distribution of data as it is possible to spread it out on several partitions
and different disks.
 Performance enhancement in the dispatching of queries, particularly for data
warehousing or decision making. The optimizer is capable of selecting a limited
quantity of rows associated with one or several partitions specified in the query.

_______________________________________________________________________
Prepared by M.A.Asad Page 48 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

This functionality is available for both indexes and tables, but cannot be implemented in
clusters.

The RULE-based optimizer does not take the partitioning of tables and indexes into
account.

II The different partitioning types:

The only partitioning mode available in version 8.0 is:

- Range Partitioning.

Version 8.1 also includes two new partitioning modes:

- Hash.
- Composite.

II.1 Range-type partitioning:

Table and index partitions are based on a list of columns allowing to store each
occurrence in a given partition.

CREATE TABLE emp_range


(empno NUMBER(4) NOT NULL,
ename VARCHAR2(10),
sal NUMBER(7,2))
PARTITION BY RANGE(EMPNO)
(partition emp_p1 VALUES LESS THAN (50),
partition emp_p2 VALUES LESS THAN (100),
partition emp_p3 VALUES LESS THAN (150),
partition emp_p4 VALUES LESS THAN (MAXVALUE));

Each partition is defined with an upper boundary. The storage location of each
occurrence is then found by comparing the partitioning key of the occurrence with this
upper boundary. This upper boundary is non-inclusive; in other words, the key of each
occurrence must be less than this limit for the record to be stored in this partition.

The storage options are set a table level and are directly inherited by the partition level if
they are not redefined by each partition.

The following views enable you to gather information on the partitions:

DBA_PART_KEY_COLUMNS
DBA_PART_COL_STATISTICS
DBA_PART_HISTOGRAMS
DBA_PART_INDEXES
DBA_IND_PARTITIONS

_______________________________________________________________________
Prepared by M.A.Asad Page 49 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
DBA_PART_TABLES
DBA_TAB_PARTITIONS
DBA_PART_LOBS
DBA_LOB_PARTITIONS

The only possible functions in the partition clause:

'VALUE LESS THAN (value1, value2 ..., valueN)'

are the TO_DATE and RPAD functions.

The NULL value can be associated to a partitioning key column and is considered to be
superior to any other possible value on this column except the MAXVALUE column.
This is a constraint. This implies the upper limit of the top-level partition must be set to
MAXVALUE; otherwise the occurrences containing a NULL value on this column
would not be able to fit in this last partition, generating an ORA-14400 error.

II.2 Hash-type partitioning:

This type of partitioning allows a better mastering of the distribution of the data in the
different partitions. These partitions are defined with the help of a hashing function
offered by Oracle. This function is then applied to a list of columns.

CREATE TABLE emp_hpart


(empno NUMBER(4) NOT NULL,
ename VARCHAR2(10),
sal NUMBER(7,2))
STORAGE (INITIAL 50k NEXT 50k)
PARTITION BY HASH(sal)
PARTITIONS 4
STORE IN (DATA01, DATA02, DATA03, DATA04);

This partitioning method is recommended in the following cases:

 It is impossible to have criteria for the distribution of data.


 It is difficult to anticipate the quantity of data for a given partition.
 It is hard to balance the load in each partition.

This partitioning method requires that the number of partitions allocated be a power of
two in order to ensure a uniform distribution of data in the different partitions.

Specific storage clauses cannot be specified for each partition. These clauses are
inherited from the tablespace in which the partitions reside.

The administration of these HASH type partitions is similar to that of the


RANGE ones except for DROP, SPLIT, and MERGE. The ADD and COALESCE
commands are used to add and drop partitions.

_______________________________________________________________________
Prepared by M.A.Asad Page 50 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
II.3 Composite-type partitioning:

This method combines the two sorting methods described previously. The first level is
based on the RANGE method and the second, finer grained, HASH is based on a hashing
function. The benefit obtained is the first level sorts the data on a logical basis while the
second balances its distribution among each one of the partitions.

CREATE TABLE emp_composite


(empno NUMBER(4) NOT NULL,
ename VARCHAR2(10),
sal NUMBER(6))
STORAGE (INITIAL 12k NEXT 12k)
PARTITION BY RANGE(empno)
SUBPARTITION BY HASH(sal) SUB-PARTITIONS 4
STORE IN (DATA01, DATA02, DATA03, DATA04)
(PARTITION p1 VALUES LESS THAN (50),
PARTITION p2 VALUES LESS THAN (100),
PARTITION p3 VALUES LESS THAN (150),
PARTITION p4 VALUES LESS THAN (MAXVALUE));

This presents a RANGE type first level partitioning based on a NUMBER type column
(EMPNO). The second level, HASH-type partitioning, is also based on a NUMBER
type column (SAL), and each of these partitions is divided into four sub-partitions.

At the RANGE level the partitioning is a logical sort. The data is then physically stored
in the sub-partitions (HASH). The storage at this level can be visualized in the
DBA_TAB_SUB-PARTITIONS view.

PARTITION_NAME SUBPARTITION_NAME POSITION TABLESPACE_NAME


--------------- -------------------- ---------- ----------------
P1 SYS_SUBP27 1 DATA01
P1 SYS_SUBP28 2 DATA02
P1 SYS_SUBP29 3 DATA03
P1 SYS_SUBP30 4 DATA04
P2 SYS_SUBP31 1 DATA01
P2 SYS_SUBP32 2 DATA02
P2 SYS_SUBP33 3 DATA03
P2 SYS_SUBP34 4 DATA04
P3 SYS_SUBP35 1 DATA01
P3 SYS_SUBP36 2 DATA02
P3 SYS_SUBP37 3 DATA03
P3 SYS_SUBP38 4 DATA04
P4 SYS_SUBP39 1 DATA01
P4 SYS_SUBP40 2 DATA02
P4 SYS_SUBP41 3 DATA03
P4 SYS_SUBP42 4 DATA04

Version 8.1 offers new views to administer this level of granularity:

_______________________________________________________________________
Prepared by M.A.Asad Page 51 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
DBA_SUBPART_COL_STATISTICS
DBA_SUBPART_HISTOGRAMS
DBA_SUBPART_KEY_COLUMNS
DBA_IND_SUB-PARTITIONS
DBA_LOB_SUB-PARTITIONS
DBA_TAB_SUB-PARTITIONS

COMPOSITE partitioning preserves the advantages of the two previous methods:

 Ease of administration tied to the RANGE type.


 Finer granularity level in the storage of data.
 Possibility of setting local indexes at subpartition level, combined with global
indexes at RANGE level.
 The HASH mode enables PDML operations on a parallel mode.

III Implementation:

All these combinations are theoretically possible:

- Partitioned table and non-partitioned index.


- Partitioned table and partitioned index.
- Non-partitioned table and non-partitioned index.
- Non-partitioned table and partitioned index.

Restrictions:

Table level:

 Clustered tables cannot be partitioned.


 A LONG or LONG RAW column in a table prevents it from being partitioned.
BLOB-type columns, on the other hand, may be partitioned (cf. chapter V).
 Index Only Tables (IOT) can only be RANGE partitioned.

Index level:

 Indexes for clusters cannot be partitioned.


 Indexes for clustered tables cannot be partitioned.
 Bitmap indexes must be locally partitioned indexes.
 Bitmap indexes associated to non-partitioned tables cannot be partitioned.
 Non-prefixed global indexes are not supported.
 Local non-prefixed unique indexes must have a partitioning key included in the
index key.

Multi-column partition keys are limited to 16.

The following types of columns cannot be part of a partition key:

- LEVEL or ROWID pseudo-types.


- Columns of the following types:

_______________________________________________________________________
Prepared by M.A.Asad Page 52 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
o Nested table
o Varray
o Object type
o Ref
o Rowid
- Different types of LOB (BLOB, CLOB, NCLOB, BFILE).

IV. Index partitioning:

Oracle Version 8 offers two main types of indexes: Local and Global.

Local indexes are equi-partitioned with the table they are based on. That means that for
each index entry residing in partition A there is a row of the table which also resides in
that same partition A.

A local index linked to a table has the following properties:

- Same number of partitions/sub-partitions.


- Same partition limits.
- Same partitioning keys.

The index partitioning is automatically maintained along with that of the associated table
in case of partition/subpartition addition, subtraction, splitting, or compacting. The
method used to partition the index is the same as that used for the corresponding table.
Global indexes are partitioned on columns which can be different than those used for the
base table. When you create this type of index, it is necessary to define, in a precise
manner, the types and limits for the partitions. Oracle does not keep up to date the index
partitions in case of modification of the base tables' partitions. The index partitions then
get an UNUSABLE status and the index has to be built.

Three index types are supported in the two groups mentioned above:

- Local prefixed.
- Local non prefixed.
- Global prefixed.

Prefixed vs. Non prefixed; what is the difference?

An index is considered prefixed if its partitioning key/keys constitute the left part of the
index key, columns being kept in the same order.

Non prefixed global indexes are not supported.

Non-partitioned indexes are considered to be global prefixed indexes.

We will use the following base table for the examples of each type of index:

CREATE TABLE emp

_______________________________________________________________________
Prepared by M.A.Asad Page 53 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
(empno NUMBER NOT NULL,
ename VARCHAR2(10) NOT NULL,
job VARCHAR2(14),
deptpno NUMBER)
PARTITION BY RANGE (empno)
(PARTITION part1 VALUES LESS THAN (30),
PARTITION part2 VALUES LESS THAN (60),
PARTITION part3 VALUES LESS THAN (MAXVALUE));

IV.1 Prefixed Local Index:

CREATE INDEX emp_idx1 ON emp(empno) LOCAL;

The index is considered as prefixed because the index key 'empno' is identical to the
partitioning key. This index is defined as 'local'. It is thus partitioned automatically by
Oracle on the same key as the emp table, namely 'empno'. There is, therefore, no need to
define again the partitioning criteria tied to the index. Oracle automatically creates three
index partitions based on the same partition limits as those of the emp table. Each of the
index keys is stored in a partition whose limits are identical to those of the base table.

The main advantage of the prefixed mode is that it enables the optimizer to ignore the
index partitions that do not match the search criteria tied to the index key.

Look at the following query:

select * from emp where empno = 62;

The optimizer avoids the first two partitions and goes directly to the third index partition.

emp_idx1 Index

empno empno empno


partitions -> 0-29 30-59 60-MAXVALUE

Index key -> empno


0-29 30-59 60-MAXVALUE

table emp -> empno

IV.2 Non-prefixed Local Index:

CREATE INDEX emp_idx2 ON emp(deptno) LOCAL;

In the above example, Oracle creates three index partitions, equi-partitioned with the
base table. The physical storage of the index keys, on the other hand, does not follow the
partitioning rule. This implies the spreading of the index keys in the different partitions.
Thus queries that include index key based predicates will force the optimizer to scan all
the index partitions in order to retrieve the selected values.

_______________________________________________________________________
Prepared by M.A.Asad Page 54 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
In the following query:

Select * from emp where deptno = X;

the optimizer must scan all three partitions to the index emp_idx2 in order to locate the
particular index key. The value specified in the query's predicate can be in any one of the
three index partitions.

emp_idx2
empno
partitions -> 0-29 30-59 60-MAXVALUE

Index Key -> deptno


55-95 56-18 57-82

Emp table -> empno

For non-prefixed unique key local indexes, it is mandatory that the index key be a subset
of the partitioning key in order for an index key to only belong to one partition at a time.

IV.3 Global prefixed index:

CREATE INDEX emp_idx3 ON emp(ename)


GLOBAL PARTITION BY RANGE (ename)
(PARTITION p1 VALUES LESS THAN ('N'),
PARTITION p2 VALUES LESS THAN (MAXVALUE));

Using Global Prefixed Indexes allows a partitioning key different from that of the
underlying table. This type of index must be prefixed. The index key is based on the
partitioning key, which enables the optimizer to select which of the partitions are
concerned by the query's predicate.

Consider the following query:

select * from emp where ename like 'B%';

The optimizer automatically selects the first index partition. On the other hand, rows that
make up this partition have ROWIDs referencing rows in other table partitions.

emp_Idx3

partitions -> ename


A-M

Index key -> ename


A-M N-MAXVALUE

emp -> empno

_______________________________________________________________________
Prepared by M.A.Asad Page 55 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Spreading index access over the different table partitions induces performance loss as
disk I/O increases. Global indexes are harder to manage than local indexes since Oracle
does not maintain the index partitioning automatically in case of table reorganization. A
change in the table's storage causes all the index's partitions to be put in UNUSABLE
status. Index rebuild is then necessary, and its duration is proportional to the size of the
table rather than to the size of one of its partitions.

IV.4 Guidelines to Partitioned Indexes:

The choice of a particular type of index is tied to the constraints of your Oracle
applications. A trade-off has to be found between performance of the jobs, availability,
and ease of administration.

The main rules to remember are as follows:

 Local indexes are easier to maintain and offer a higher availability than global
indexes.
 Prefixed indexes enable the optimizer to limit the probe to the partitions directly
concerned by the query predicates.
 Local prefixed indexes and global indexes are more adapted to an OLTP styles of
use whereas local non-prefixed indexes are better adapted to data-warehousing
and decision making environments.

Ask yourself the following questions before you choose a particular type of partitioned
index:

If your index is partitioned on a left prefix of the index columns,


then ===> Local prefixed

If you want to create a unique index, whose columns do not include


your partitioning keys ===> Global prefixed

If you can use parallel access to the different partitions and mainly
work in a DSS environment ===> Local non-prefixed

If you work in an OLTP environment ===> Global prefixed

Local non-prefixed indexes are particularly useful for historical type databases in which
the data is partitioned on a DATE type criteria. This index is useful in efficiently probing
the database with particular criteria, different from a date. Maintenance operations tied
to adding or dropping a partition are simpler.

V. LOB partitioning:

Partitioned tables may have LOB type columns as of 8.1, but these columns may not be
used as partitioning keys.

All types of partitioning support the different types of LOBs:

_______________________________________________________________________
Prepared by M.A.Asad Page 56 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
- BLOB
- CLOB
- NCLOB
- BFILE

In the case of BFILE type LOBs, only the reference of these columns is stored in the
tables.

All LOB type columns automatically generate a data segment along with an associated
index that enables the access to the different parts of the LOB. The storage clauses tied
to the LOB as well as to its associated index can be different from those of the associated
table.

For a partitioned table with a LOB type column, Oracle creates as many data and index
segments as there are table partitions or sub-partitions. These different segments are
equi-partitioned with the corresponding table partitions.

CREATE TABLE emp_lob


(empno NUMBER(4) NOT NULL,
ename VARCHAR2(10),
sal NUMBER(7,2),
image BLOB)
LOB (image) STORE AS IMAGE_LOB
(CHUNK 1 PCTVERSION 20 NOCACHE LOGGING TABLESPACE T_LOB
STORAGE (INITIAL 12k NEXT 12K PCTINCREASE 0))
TABLESPACE USERS
PARTITION BY RANGE(empno)
(PARTITION emp_p1 VALUES LESS THAN (50),
PARTITION emp_p2 VALUES LESS THAN (100),
PARTITION emp_p3 VALUES LESS THAN (150),
PARTITION emp_p4 VALUES LESS THAN (MAXVALUE));

In the above example, the table is partitioned in four partitions. Each one of these
partitions is associated with a LOB partition. These LOB partitions are themselves
broken into a data partition and an index partition. This organization is visible in the
Oracle dictionary via the following queries:

select partition_position, tablespace_name, partition_name


from dba_tab_partitions
where table_name = 'EMP_LOB';

PARTITION_POSITION TABLESPACE_NAME PARTITION_NAME


------------------ ------------------------------ ---------------
1 USERS EMP_P1
2 USERS EMP_P2
3 USERS EMP_P3
4 USERS EMP_P4

_______________________________________________________________________
Prepared by M.A.Asad Page 57 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

select column_name, segment_name, index_name


from dba_lobs
where table_name = 'EMP_LOB';

COLUMN_NAME SEGMENT_NAME INDEX_NAME


--------------- --------------- -------------------------
IMAGE IMAGE_LOB SYS_IL0000012665C00004$$

The index segment name is implicitly generated by ORACLE. The two objects,
IMAGE_LOB and SYS_IL0000012665C00004$$, only exist virtually in the Oracle
dictionary, but physically exist in the form of partitions.

select column_name, lob_name, partition_name, lob_partition_name,


lob_indpart_name, tablespace_name
from dba_lob_partitions
where table_name = 'EMP_LOB';

COLUMN_NAME LOB_NAME PARTITION LOB_PARTITION LOB_INDPART


TABLESPACE
----------- ---------- --------- ------------- ----------- ----------
IMAGE IMAGE_LOB EMP_P1 SYS_LOB_P63 SYS_IL_P67 T_LOB
IMAGE IMAGE_LOB EMP_P2 SYS_LOB_P64 SYS_IL_P68 T_LOB
IMAGE IMAGE_LOB EMP_P3 SYS_LOB_P65 SYS_IL_P69 T_LOB
IMAGE IMAGE_LOB EMP_P4 SYS_LOB_P66 SYS_IL_P70 T_LOB

select column_name, lob_name, lob_index_name, def_chunk,


def_pctversion
from dba_part_lobs
where table_name = 'EMP_LOB';

COLUMN LOB_NAME LOB_INDEX_NAME DEF_CHUNK


DEF_PCTVERSION
------ ---------- ------------------------ ---------- --------------
IMAGE IMAGE_LOB SYS_IL0000012665C00004$$ 1 20

select index_name, partitioning_type "TYPE", partition_count "COUNT",


locality, alignment
from dba_part_indexes
where table_name = 'EMP_LOB';

INDEX_NAME TYPE COUNT LOCALITY ALIGNMENT


------------------------ ----------- ------- -------- ----------
SYS_IL0000012665C00004$$ RANGE 4 LOCAL NON_PREFIXED

_______________________________________________________________________
Prepared by M.A.Asad Page 58 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
select high_value, partition_name "NAME",
partition_position "POSITION",
status, tablespace_name "TNAME"
from dba_ind_partitions
where index_name = 'SYS_IL0000012665C00004$$';

HIGH_VALUE NAME POSITION STATUS TABLESPACE


---------- ----------- -------- ------ ----------
50 SYS_IL_P67 1 USABLE T_LOB
100 SYS_IL_P68 2 USABLE T_LOB
150 SYS_IL_P69 3 USABLE T_LOB
MAXVALUE SYS_IL_P70 4 USABLE T_LOB

As for the other types of partitions, the storage clauses may be defined at the level of the
table or directly in the definition of each partition. Each LOB partition can have its own
storage clauses as well as specific tablespace.

Index partitions for LOBs are always stored in the same tablespace as the partitions for
the LOB data partitions. It is not possible to specify a storage clause for partitions
concerning index LOBS as those are always linked to the corresponding data partitions
or to the tablespace in which they reside.

In the case of BFILE type columns, Oracle only stores the value of the LOCATOR that
references an external file. Oracle therefore only partitions the BFILE column in the
same manner it would use for a VARCHAR2 type column.

VI. IOT partitioning:

Index Organized Tables (IOT) can now be partitioned as of version 8.1.

BACKGROUND: IOTs have a BTREE index structure where the leaves of this index
contain the actual rows of the table. The rows of the table are sorted on the primary key,
which is essential to the creation of each IOT. It is possible to leave an overflow zone
when you create an IOT containing any part of the row that does not fit in the primary
key and that has to be stored in a particular area. The aim of this additional segment is to
limit the size of the rows stored in each IOT block.

IOT partitioning is only possible under certain conditions:

- The partitioning must be of RANGE type.


- The partitioning key(s) must be a subset of the primary key.
- The overflow zones must be equi-partitioned with IOT partitions.

Oracle imposes that the partitioning key be a subset of the primary key or included in the
latter, which allows unique constraint checking amid a partition. Moreover if this
partitioning key is prefixed, then the queries based on the partitioning predicate directly
retrieve the rows stored on the primary key.

Consider for example the following IOT:

_______________________________________________________________________
Prepared by M.A.Asad Page 59 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

CREATE TABLE emp_iot


(empno NUMBER(4) PRIMARY KEY,
ename VARCHAR2(10),
sal NUMBER(7,2))
ORGANIZATION INDEX INCLUDING ename OVERFLOW
PARTITION BY RANGE(empno)
(partition emp_p1 VALUES LESS THAN (50) TABLESPACE data01,
partition emp_p2 VALUES LESS THAN (100) TABLESPACE data02,
partition emp_p3 VALUES LESS THAN (150) TABLESPACE data03,
partition emp_p4 VALUES LESS THAN (MAXVALUE) TABLESPACE data04);

Each IOT partition generates two partitions: a data partition and an overflow partition.
This overflow partition contains the data associated to the rows beyond the 'ename'
column including that very column.

This organization can be visualized with the following dictionary views:

select table_name, iot_name, iot_type, partitioned


from dba_tables
where table_name like '%IOT%';

TABLE_NAME IOT_NAME IOT_TYPE PARTITIONED


-------------------- ---------- ------------ -----------
EMP_IOT IOT YES
SYS_IOT_OVER_12770 EMP_IOT IOT_OVERFLOW YES

select table_name, partitioning_type "TYPE", partition_count "COUNT",


def_tablespace_name
from dba_part_tables
where table_name like '%IOT%';

TABLE_NAME TYPE COUNT DEF_TABLESPACE_NAME


------------------ ----------- ------- -------------------
SYS_IOT_OVER_12770 RANGE 4 USERS
EMP_IOT RANGE 4

select index_name, index_type, table_name, uniqueness, partitioned


from dba_indexes
where table_name like '%IOT%';

INDEX_NAME INDEX_TYPE TABLE_NAME UNIQUENES PARTITIONED


------------------ ----------- ----------- --------- -----------
SYS_IOT_TOP_12770 IOT - TOP EMP_IOT UNIQUE YES

_______________________________________________________________________
Prepared by M.A.Asad Page 60 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

select index_name, partition_name, high_value, tablespace_name


from dba_ind_partitions
where index_name like '%IOT%';

INDEX_NAME PARTITION_NAME HIGH_VALUE TABLESPACE_NAME


----------------- -------------- ---------- ---------------
SYS_IOT_TOP_12770 EMP_P1 50 DATA01
SYS_IOT_TOP_12770 EMP_P2 100 DATA02
SYS_IOT_TOP_12770 EMP_P3 150 DATA03
SYS_IOT_TOP_12770 EMP_P4 MAXVALUE DATA04

select table_name, partition_position, partition_name, tablespace_name


from dba_tab_partitions
where table_name like '%IOT%';

TABLE_NAME PARTITION_POSITION PARTITION_NAME


TABLESPACE_NAME
-------------------- ------------------ --------------- ---------------
SYS_IOT_OVER_12770 1 EMP_P1 USERS
SYS_IOT_OVER_12770 2 EMP_P2 USERS
SYS_IOT_OVER_12770 3 EMP_P3 USERS
SYS_IOT_OVER_12770 4 EMP_P4 USERS
EMP_IOT 1 EMP_P1
EMP_IOT 2 EMP_P2
EMP_IOT 3 EMP_P3
EMP_IOT 4 EMP_P4

In this case, the view DBA_TAB_PARTITIONS shows that the data segments tied to the
overflow are stored in the creator's default tablespace.
This was due to no tablespace being specified for the overflow segment, and the table has
no default tablespace specified. When there is a default tablespace for the table, then the
overflow segments are created in that tablespace.

Additional indexes may be specified on other columns of the IOT (local prefixed, local
non prefixed, local prefixed, or global prefixed).

VII. Partition Maintenance:

Consider the following Test table:

CREATE TABLE test


(empno NUMBER(4) NOT NULL,
ename VARCHAR2(10),
sal NUMBER(7,2))

_______________________________________________________________________
Prepared by M.A.Asad Page 61 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
PARTITION BY RANGE(EMPNO)
(partition emp_p1 VALUES LESS THAN (50),
partition emp_p2 VALUES LESS THAN (100),
partition emp_p3 VALUES LESS THAN (150),
partition emp_p4 VALUES LESS THAN (200));

select partition_position, partition_name, tablespace_name


from dba_tab_partitions
where table_name = 'TEST';

PARTITION_POSITION PARTITION_NAME TABLESPACE_NAME


------------------ ----------------------- ----------------------
1 EMP_P1 USERS
2 EMP_P2 USERS
3 EMP_P3 USERS
4 EMP_P4 USERS

VII.1 Moving partitions:

This ALTER TABLE option allows the transfer of the table partition from one tablespace
to another. The status of any index partitions tied to this partition become UNUSABLE.
In the case of a global index, the whole index has to be rebuilt.

ALTER TABLE test MOVE PARTITION emp_p1 tablespace tbs1;

Querying on DBA_TAB_PARITIONS shows the moved partition:

PARTITION_POSITION PARTITION_NAME TABLESPACE_NAME


------------------ ----------------------- ----------------------
1 EMP_P1 TBS1
2 EMP_P2 USERS
3 EMP_P3 USERS
4 EMP_P4 USERS

VII.2 Adding partitions:

The ADD PARITION clause allows you to add an extra partition beyond the last
partition as long as the upper limit is not equal to MAXVALUE. Should you specify a
value that is equal, the add partition statement would fail.

Let's add an extra partition at the end of the TEST table using the
ADD PARTITION:

ALTER TABLE test ADD PARTITION emp_p5 values less than (300);

Querying on DBA_TAB_PARTITIONS, this time for the high value of each partition:

_______________________________________________________________________
Prepared by M.A.Asad Page 62 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

select partition_name, high_value, tablespace_name


from dba_tab_partitions
where table_name = 'TEST';

PARTITION_NAME HIGH_VALUE TABLESPACE_NAME


----------------------- ---------------------- ---------------
EMP_P1 50 TBS1
EMP_P2 100 USERS
EMP_P3 150 USERS
EMP_P4 200 USERS
EMP_P5 300 USERS

Note the tablespace for the new partition.

In the case of indexes, partitions can only be added to global indexes. The upper limit of
a global index always being MAXVALUE implies that SPLIT is the only possible
command (see section VII.5).

VII.3 Dropping partitions:

The DROP PARTITION clause allows the withdrawal of a table or global index partition.
The DROP of a table partition causes the status of all the partitions of the global index to
become UNUSABLE. A complete rebuild of the index has to occur to modify this status.

ALTER TABLE test DROP PARTITION emp_p1;

PARTITION_NAME HIGH_VALUE TABLESPACE_NAME


----------------------- ---------------------- ---------------
EMP_P2 100 USERS
EMP_P3 150 USERS
EMP_P4 200 USERS
EMP_P5 300 USERS

This functionality is not available for HASH partitioned tables, which have to use the
COALESCE command instead (see section VII.8).

VII.4 Truncate partitions:

Discards all the rows of a table partition while the storage allocated is preserved. This
option is not available for indexes. Local index partitions are automatically kept up to
date by Oracle. In the case of global indexes, the status of all the partitions becomes
UNUSABLE.

ALTER TABLE test TRUNCATE PARTITION emp_p2;

_______________________________________________________________________
Prepared by M.A.Asad Page 63 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

VII.5 Splitting partitions:

The SPLIT PATITION clause enables you to create from an original partition two new
partitions, each with a new segment and new physical attributes, and new initial extents.
The segment associated with the old partition is discarded. The associated index
partitions, global and local, become UNUSABLE.

ALTER TABLE test


SPLIT PARTITION EMP_P5 AT ( 250 )
INTO (partition EMP_P5,
partition EMP_P6);

Looking at the values now:

PARTITION_NAME HIGH_VALUE TABLESPACE_NAME


----------------------- ---------------------- ---------------
EMP_P2 100 USERS
EMP_P3 150 USERS
EMP_P4 200 USERS
EMP_P6 300 USERS
EMP_P5 250 USERS

The EMP_P5 partition is divided into two distinct partitions, EMP_P5 and EMP_P6.
The two partitions are redefined on the following values: 200-249 and 250-300. This
functionality also works on global indexes.

The SPLIT command cannot be used on HASH type partitions; the ADD command has
to be used instead.

Consider the emp_hpart table described previously in section II.2, which is distributed
over four partitions:

PARTITION_NAME PARTITION_POSITION TABLESPACE_NAME


--------------- ------------------ ---------------
SYS_P69 1 DATA01
SYS_P70 2 DATA02
SYS_P71 3 DATA03
SYS_P72 4 DATA04

A query on DBA_SEGMENTS shows the location of the partitions:

select segment_name, partition_name, tablespace_name,


header_file, header_block
from dba_segments
where segment_name='TEST';

SEGMENT_NAME PARTITION_NAME TABLESPACE_NAME HEADER_FILE


HEADER_BLOCK

_______________________________________________________________________
Prepared by M.A.Asad Page 64 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
--------------- --------------- --------------- ----------- ------------
EMP_HPART SYS_P69 DATA01 8 21
EMP_HPART SYS_P70 DATA02 9 21
EMP_HPART SYS_P71 DATA03 10 2
EMP_HPART SYS_P72 DATA04 11 2

The contents of the first partition P1 are the following:

EMPNO ENAME SAL


---------- ---------- ----------
303 TEST 10302
306 TEST 10305
310 TEST 10309
325 TEST 10324
326 TEST 10325
332 TEST 10331
338 TEST 10337
341 TEST 10340
343 TEST 10342
347 TEST 10346
348 TEST 10347
349 TEST 10348
350 TEST 10349
360 TEST 10359
361 TEST 10360
365 TEST 10364
368 TEST 10367
370 TEST 10369
373 TEST 10372
380 TEST 10379
385 TEST 10384
386 TEST 10385
396 TEST 10395
400 TEST 10399
401 TEST 10400

25 rows selected.

The following command adds a new partition P5 and redistributes the rows of
partition P1 between P1 and P5.

Using Union All Views Over Partitioned Tables in Oracle 8.0

Partition tables do not provide equivalent functionality to manual partition views.


Because of this, manual partitions may be built over the top of the partition tables to
provide this functionality as follows:

_______________________________________________________________________
Prepared by M.A.Asad Page 65 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Allow Optimizer to generate different access paths for individual partitions within a
partitioned table.

When the Optimizer is presented with a partitioned table, the optimizer chooses one
access path for all partitions. This access path may be inappropriate with skewed data,
unevenly sized partitions, etc. A workaround is to create UNION ALL views over the top
of the partitioned tables. This allows the optimizer to select different access paths for
each underlying table. Otherwise, the optimizer treats the partition table as a single entity
and only produces a single access path for the table. This is a valid approach until the
optimizer can generate partition level access plans.

Example:

Partitioned Table P1 has 2 partitions

P1_1 contains values < 10000 and 40,000 rows


P1_2 contains values between 10000 and 20000 and 1 row

Consider the following select:

select count(COL1) from P1


where COL1 between 5000 and 12001;

The explain plan shows an index access for BOTH partitions.

Query Plan

SELECT STATEMENT [CHOOSE] Cost=31


SORT AGGREGATE
PARTITION CONCATENATED
INDEX RANGE SCAN P1I Ct=31 Cd=23340 Bs=536820

However, Partition 2, since it contains only 1 row would be better accessed by using a
Full table scan:

select count(COL1) from P1 partition(P1_2)


where COL1 between 5000 and 12001;

Query Plan

SELECT STATEMENT [CHOOSE] Cost=1


SORT AGGREGATE
TABLE ACCESS FULL P1 [ANALYZED] Ct=1 Cd=1 Bs=23

Partition P1 has 40,000 rows in so a index access may well be ok.

select count(COL1) from P1 partition(P1_1)


where COL1 between 5000 and 12001;

_______________________________________________________________________
Prepared by M.A.Asad Page 66 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Query Plan

SELECT STATEMENT [CHOOSE] Cost=14


SORT AGGREGATE
INDEX RANGE SCAN P1I Ct=14 Cd=20023 Bs=460529

So to get both access paths, create a union all view as follows:

create or replace view pvw as


select col1,col2 from p1 partition(p1_1)
union all
select col1,col2 from p1 partition(p1_2)

Selecting from the view gives the desired result:

select count(COL1) from pvw


where COL1 between 5000 and 12001;

Query Plan

SELECT STATEMENT [CHOOSE] Cost=10


SORT AGGREGATE
VIEW PVW Ct=10 Cd=27353 Bs=629119
UNION-ALL PARTITION
INDEX RANGE SCAN P1I Ct=14 Cd=20023 Bs=260299
TABLE ACCESS FULL P1 [ANALYZED] Ct=1 Cd=1 Bs=13

Partitions on remote databases

Partition tables cannot consist of tables on remote sites. By using union all views you can
combine remote tables with local tables so that the object selected from looks as though
it is on the same location.

Examples about Insert into Range Partitioned Tables


Single column partition key

Each row will be inserted in a partition if and only if the partition key is less than the
partition bound defined with the partition definition.

'VALUES LESS THAN' specifies a non-inclusive upper bound for partition. An insert
fails with an ORA-1440 error if the partition key is higher than or equal to the partition
bound for the highest partition.
For avoiding this, you must use VALUES LESS THAN (MAXVALUE) bound to allow
any values to be inserted in at least one partition. A special trap is when you are going to
use a NULL value as a partition key value for a new insert. You must have specified the
MAXVALUE on the last partition bound. If you don’t follow this rule, you will get the
ORA-1440 error, as the NULL value is considered as the greatest value except
MAXVALUE.

_______________________________________________________________________
Prepared by M.A.Asad Page 67 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Consider the following table definition :

CREATE TABLE Essai


(col1 NUMBER(5),
col2 VARCHAR2(30),
col3 NUMBER(8),
col4 NUMBER(5))
PARTITION BY RANGE (col4)
(PARTITION p1 VALUES LESS THAN (26) TABLESPACE data01,
PARTITION p2 VALUES LESS THAN (53) TABLESPACE data02,
PARTITION p3 VALUES LESS THAN (MAXVALUE));

II. Multi-column partition key

A row that is being inserted into a partitioned table is compared to partition keys to
determine in which partition should be stored the row. Oracle Server will evaluate
vectors (list of values) from left to right (following ANSI SQL2 vector ordering rules)
comparing values in corresponding positions. Once a differing value is found then the
next values are ignored.

From Oracle8i Server Concepts 11-22 read the rule on MultiColumn Partitioning:

"In mathematical terms, for vectors V1 and V2 which contain the same number of
values,
Vx[i] is the ith value in Vx.
Assuming that V1[i] and V2[i] have compatible datatypes:

V1 = V2 if and only if V1[i] = V2[i] for all i


V1 < V2 if and only if V1[i] = V2[i] for all i<n and V1[n] < V2[n] for some n
V1 > V2 if and only if V1[i] = V2[i] for all i<n and V1[n] > V2[n] for some n."

Oracle RDBMS will compare the bound vectors associated with each partition definition,
with the new vector which must be added. The comparison must begin with the first
partition’s vector definition, and continue by the following when the new vector is
considered greater.
This process will be finished when the partition vector is considered greater than the new
inserted vector.

These rules may be applied also for updated rows.

Consider the table below with three partitions. The partition key is based on 3 columns :
Year, Month, Day:

CREATE TABLE Sales


(Id NUMBER(3),
Name VARCHAR2(25),
Amount NUMBER(7,2),
Year NUMBER(4),
Month NUMBER(2),

_______________________________________________________________________
Prepared by M.A.Asad Page 68 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Day NUMBER(2))
PARTITION BY RANGE (Year,Month,Day)
(PARTITION P1 VALUES LESS THAN (1999, 01, 32),
PARTITION P2 VALUES LESS THAN (1999, 02, 29),
PARTITION P3 VALUES LESS THAN (MAXVALUE, MAXVALUE, MAXVALUE));

Now consider the following 4 inserts in this table and the targeted partitions :

1. INSERT INTO Sales(Year, Month, Day) VALUES (1999, 02, 17) stored in P2.

2. INSERT INTO Sales(Year, Month, Day) VALUES (1999, 02, 33) stored in P3

3. INSERT INTO Sales(Year, Month, Day) VALUES (1997, 01, 01) stored in P1

4. INSERT INTO Sales(Year, Month, Day) VALUES (1999, 02, 29) stored in P3

We will detail the algorithm followed for the first insertion above:

- ‘1999’ is equal to the first key value of P1, then we continue to compare with P1.
- ‘02’ is greater than second key of P1, then the new insert vector is greater than
P1. We must now evaluate P2.
- ‘02’ is equal to the second key of P2, then we continue to compare with P2.
- ‘17’ is lower than the third key, ‘29’, of P2, then the conclusion will be that
the new insert vector is lower than
P2. Hence, The new vector will be loaded on P2.

III. Updatable Partition keys


------------------------

In 8.0 versions, Updates affecting partitioning keys were disallowed and return an ORA-
14402.
From 8.1 version, a new CREATE/ALTER table option has been introduced, ENABLE
ROW MOVEMENT, which is disabled by default.
When ROW MOVEMENT is enabled, Updates on key partition values are allowed,
resulting in a row migration into the appropriate partition.

Example:

SQL> UPDATE Essai SET col4=33 WHERE col4=12;


UPDATE Essai SET col4=33 WHERE col4=12
*
ERREUR Ã la ligne 1 :
ORA-14402: updating partition key column would cause a partition change

SQL> ALTER table Essai ENABLE ROW MOVEMENT;

Table modified.

_______________________________________________________________________
Prepared by M.A.Asad Page 69 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL> UPDATE Essai SET col4=33 WHERE col4=12;

1 row updated.

"Update Global Indexes" Allows Automatic Global Index Maintenance During


DDL

This bulletin explains:

The new clause "UPDATE GLOBAL INDEXES" and when it can be used in perations
performing DDL on table partitions, depending on the type of partitioning and the type of
DDL performed:
* range
* hash
* range hash (composite)
* list

Which performance considerations would lead to choosing REBUILD INDEX rather


than use UPDATE GLOBAL INDEXES.

1. Set up a RANGE partitioned table and a GLOBAL index:

SQL> create table orders (


2 order_no number,
3 part_no varchar2(40),
4 ord_date date
5 )
6 partition by range (ord_date)
7 (partition Q1 values less than (TO_DATE('01-APR-1999','DD-MON-YYYY')),
8 partition Q2 values less than (TO_DATE('01-JUL-1999','DD-MON-YYYY')),
9 partition Q3 values less than (TO_DATE('01-OCT-1999','DD-MON-YYYY')),
10 partition Q4 values less than (TO_DATE('03-JAN-2000','DD-MON-YYYY'))
11 );

Table created.

SQL> create index orders_global_idx


2 on orders(ord_date)
3 global partition by range (ord_date)
4 (partition GLOBAL1 values less than (TO_DATE('01-APR-1999','DD-MON-
YYYY')),
5 partition GLOBAL2 values less than (TO_DATE('01-SEP-1999','DD-MON-
YYYY')),
6 partition GLOBAL3 values less than (TO_DATE('01-DEC-2000','DD-MON-
YYYY')),
7 partition GLOBAL4 values less than (MAXVALUE)
8 );

Index created.

_______________________________________________________________________
Prepared by M.A.Asad Page 70 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

SQL> select substr(index_name,1,20) index_name, substr(partition_name,1,20)


2 part_name , status
3 from dba_ind_partitions
4 where index_name= 'ORDERS_GLOBAL_IDX' order by partition_name;

INDEX_NAME PART_NAME STATUS


-------------------- -------------------- --------
ORDERS_GLOBAL_IDX GLOBAL1 USABLE
ORDERS_GLOBAL_IDX GLOBAL2 USABLE
ORDERS_GLOBAL_IDX GLOBAL3 USABLE
ORDERS_GLOBAL_IDX GLOBAL4 USABLE

SQL> insert into orders values (1,100,TO_DATE('02-FEB-1999','DD-MON-YYYY'));

2. Set up a HASH partitioned table and a GLOBAL index:

SQL> CREATE TABLE emp_hpart(


2 empno NUMBER(4) NOT NULL,
3 ename VARCHAR2(10),
4 sal NUMBER(7,2))
5 PARTITION BY HASH(sal)
6 (PARTITION H1, PARTITION H2, PARTITION H3, PARTITION H4);

Table created.

SQL> CREATE INDEX emp_global_HASH_idx ON emp_hpart(ename)


2 GLOBAL PARTITION BY RANGE (ename)
3 (PARTITION p1 VALUES LESS THAN ('N') ,
4 PARTITION p2 VALUES LESS THAN (MAXVALUE));
Index created.

SQL> select substr(index_name,1,20) index_name,


2 substr(partition_name,1,20) part_name,status
3 from dba_ind_partitions
4 where index_name= 'EMP_GLOBAL_HASH_IDX' order by partition_name;

INDEX_NAME PART_NAME STATUS


-------------------- -------------------- --------
EMP_GLOBAL_HASH_IDX P1 USABLE
EMP_GLOBAL_HASH_IDX P2 USABLE

SQL> insert into emp_hpart values (1,'AAA',100);

3. Set up a COMPOSITE partitioned table and a GLOBAL index:

_______________________________________________________________________
Prepared by M.A.Asad Page 71 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

SQL> CREATE TABLE emp_composite(


2 empno NUMBER(4) NOT NULL,
3 ename VARCHAR2(10),
4 sal NUMBER(6))
5 PARTITION BY RANGE(empno)
6 SUBPARTITION BY HASH(sal) SUB-PARTITIONS 4
7 (PARTITION p1 VALUES LESS THAN (50),
8 PARTITION p2 VALUES LESS THAN (100),
9 PARTITION p3 VALUES LESS THAN (150),
10 PARTITION p4 VALUES LESS THAN (MAXVALUE));

Table created.

SQL> CREATE INDEX emp_global_composite_idx ON emp_composite(ename)


2 GLOBAL PARTITION BY RANGE (ename)
3 (PARTITION p1 VALUES LESS THAN ('N') ,
4 PARTITION p2 VALUES LESS THAN (MAXVALUE));
Index created.

SQL> select substr(index_name,1,20) index_name,


2 substr(partition_name,1,20) part_name,status
3 from dba_ind_partitions
4 where index_name= 'EMP_GLOBAL_COMPOSITE_IDX' order by partition_name;

INDEX_NAME PART_NAME STATUS


-------------------- -------------------- --------
EMP_GLOBAL_COMPOSITE P1 USABLE
EMP_GLOBAL_COMPOSITE P2 USABLE

SQL> insert into emp_composite values (1,'AAA',100);

4. Set up a LIST partitioned table and a GLOBAL index:

SQL> CREATE TABLE locations (


2 location_id NUMBER, street_address VARCHAR2(80), postal_code CHAR(12),
3 city VARCHAR2(80), state_province CHAR(2), country_id VARCHAR2(20))
4 PARTITION BY LIST (state_province)
5 (PARTITION region_east
6 VALUES ('MA','NY','CT','NH','ME','MD','VA','PA','NJ'),
7 PARTITION region_west
8 VALUES ('CA','AZ','NM','OR','WA','UT','NV','CO'),
9 PARTITION region_south
10 VALUES ('TX','KY','TN','LA','MS','AR','AL','GA'),
11 PARTITION region_central
12 VALUES ('OH','ND','SD','MO','IL','MI',NULL,'IA'));

_______________________________________________________________________
Prepared by M.A.Asad Page 72 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Table created.

SQL> create index loc_global_idx


2 on locations (state_province)
3 global partition by range (state_province)
4 (partition p1 values less than ('NV'),
5 partition p2 values less than (maxvalue));

Index created.

SQL> INSERT INTO locations VALUES


2 ( 1000,'1297 Via Cola di Rie','00989','Roma',NULL,'IT');
1 row created.

SQL> select substr(index_name,1,20) index_name, substr(partition_name,1,20)


2 part_name , status
3 from dba_ind_partitions
4 where index_name= 'LOC_GLOBAL_IDX' order by partition_name;

INDEX_NAME PART_NAME STATUS


-------------------- -------------------- --------
LOC_GLOBAL_IDX P1 USABLE
LOC_GLOBAL_IDX P2 USABLE

Version 8/8i : Without the UPDATE GLOBAL INDEXES clause

SQL> select substr(index_name,1,20) index_name,


2 substr(partition_name,1,20) part_name,status
3 from dba_ind_partitions
4 where index_name= 'ORDERS_GLOBAL_IDX' order by partition_name;

INDEX_NAME PART_NAME STATUS


-------------------- -------------------- --------
ORDERS_GLOBAL_IDX GLOBAL1 USABLE
ORDERS_GLOBAL_IDX GLOBAL2 USABLE
ORDERS_GLOBAL_IDX GLOBAL3 USABLE
ORDERS_GLOBAL_IDX GLOBAL4 USABLE

SQL> ALTER TABLE orders DROP PARTITION q2;


Table altered.

SQL> select substr(index_name,1,20) index_name, substr(partition_name,1,20)


2 part_name, status
3 from dba_ind_partitions
4 where index_name= 'ORDERS_GLOBAL_IDX' order by partition_name;

INDEX_NAME PART_NAME STATUS

_______________________________________________________________________
Prepared by M.A.Asad Page 73 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
-------------------- -------------------- --------
ORDERS_GLOBAL_IDX GLOBAL1 UNUSABLE
ORDERS_GLOBAL_IDX GLOBAL2 UNUSABLE
ORDERS_GLOBAL_IDX GLOBAL3 UNUSABLE
ORDERS_GLOBAL_IDX GLOBAL4 UNUSABLE

=> In version pre-9i, any DDL operation on a partition of a partitioned table


would invalidate the use of GLOBAL indexes, if the corresponding partition
isn't empty.

=> In 9i, the UPDATE GLOBAL INDEXES clause allows automatic maintenance of
GLOBAL indexes while performing DDL operations on a partition of the table.
Some combinations are nevertheless not allowed.

UPDATE GLOBAL INDEXES clause and ADD PARTITION clause

1. RANGE partitioned tables:

SQL> ALTER TABLE orders ADD PARTITION q5


2 values less than (TO_DATE('03-JUN-2000','DD-MON-YYYY'))
3 UPDATE GLOBAL INDEXES;
ALTER TABLE orders ADD PARTITION q5
*
ERROR at line 1:
ORA-30564: Index maintainence clause not allowed for ADD partition to RANGE
partitioned tables

The clause UPDATE GLOBAL INDEXES is allowed only for adding a


=> partition to a HASH partitioned table
=> subpartition to a composite partitioned table

Use the conventional way for RANGE partitioned tables |

SQL> ALTER TABLE orders ADD PARTITION q5


2 values less than (TO_DATE('03-JUN-2000','DD-MON-YYYY'));
Table altered.

SQL> @sel

INDEX_NAME PART_NAME STATUS


-------------------- -------------------- --------
ORDERS_GLOBAL_IDX GLOBAL1 USABLE
ORDERS_GLOBAL_IDX GLOBAL2 USABLE
ORDERS_GLOBAL_IDX GLOBAL3 USABLE
ORDERS_GLOBAL_IDX GLOBAL4 USABLE

2. HASH partitioned tables:

_______________________________________________________________________
Prepared by M.A.Asad Page 74 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Use UPDATE GLOBAL INDEXES way with HASH partitioned tables or the global
index is left UNUSABLE

SQL> ALTER TABLE emp_hpart ADD PARTITION q5


2 UPDATE GLOBAL INDEXES;

Table altered.

SQL> @sel

INDEX_NAME PART_NAME STATUS


-------------------- -------------------- --------
EMP_GLOBAL_HASH_IDX P1 USABLE
EMP_GLOBAL_HASH_IDX P2 USABLE

3. COMPOSITE partitioned tables:

Use UPDATE GLOBAL INDEXES way with COMPOSITE partitioned tables or


the global index is left UNUSABLE

SQL> ALTER TABLE emp_composite MODIFY PARTITION p1 add subpartition h5


2 UPDATE GLOBAL INDEXES;
Table altered.

SQL> @sel

INDEX_NAME PART_NAME STATUS


-------------------- -------------------- --------
EMP_GLOBAL_COMPOSITE P1 USABLE
EMP_GLOBAL_COMPOSITE P2 USABLE

4. LIST partitioned tables

Use the conventional way for LIST partitioned tables

SQL> alter table locations ADD


2 partition nomansland values ('XX');
Table altered.

SQL> @sel

INDEX_NAME PART_NAME STATUS


-------------------- -------------------- --------
LOC_GLOBAL_IDX P1 USABLE
LOC_GLOBAL_IDX P2 USABLE

UPDATE GLOBAL INDEXES clause and DROP PARTITION clause

_______________________________________________________________________
Prepared by M.A.Asad Page 75 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

1. RANGE partitioned tables:

Use UPDATE GLOBAL INDEXES way with RANGE partitioned tables or the
global index is left UNUSABLE

SQL> ALTER TABLE orders DROP PARTITION q2 UPDATE GLOBAL INDEXES;


Table altered.

2. HASH partitioned tables:

No DROP allowed with HASH partitioned tables.


Use COALESCE instead. (See at the end of the bulletin)

3. COMPOSITE partitioned tables:

Use UPDATE GLOBAL INDEXES way with COMPOSITE partitioned tables or


the global index is left UNUSABLE

SQL> ALTER TABLE emp_composite DROP PARTITION p2


2 UPDATE GLOBAL INDEXES;

Table altered.

4. LIST partitioned tables:

Use UPDATE GLOBAL INDEXES way with LIST partitioned tables or the global
index is left UNUSABLE

SQL> alter table locations DROP PARTITION


2 region_south
3 UPDATE GLOBAL INDEXES;
Table altered.

UPDATE GLOBAL INDEXES clause and SPLIT PARTITION clause


1. RANGE partitioned tables

Use UPDATE GLOBAL INDEXES way with RANGE partitioned tables or the global
index is left UNUSABLE

SQL> ALTER TABLE orders SPLIT PARTITION q3 AT


2 (TO_DATE('15-SEP-1999','DD-MON-YYYY'))
3 INTO (PARTITION q3_1, PARTITION q3_2)
4 UPDATE GLOBAL INDEXES;

Table altered.

2. HASH partitioned tables

_______________________________________________________________________
Prepared by M.A.Asad Page 76 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

No SPLIT allowed with HASH partitioned tables.


Use ADD instead. (See above)

3. COMPOSITE partitioned tables

Use UPDATE GLOBAL INDEXES way with COMPOSITE partitioned tables or


the global index is left UNUSABLE

SQL> ALTER TABLE emp_composite SPLIT PARTITION p2 AT (80)


2 INTO (PARTITION p2_1, PARTITION p2_2)
3 UPDATE GLOBAL INDEXES;
Table altered.

4. LIST partitioned tables:

Use UPDATE GLOBAL INDEXES way with LIST partitioned tables or the global index
is left UNUSABLE

SQL> alter table locations SPLIT PARTITION region_east


2 VALUES ('MA','NJ')
3 INTO (PARTITION region_east_1, PARTITION region_east_2)
4 UPDATE GLOBAL INDEXES;
Table altered.

UPDATE GLOBAL INDEXES clause and MERGE PARTITION clause

1. RANGE partitioned tables

Use UPDATE GLOBAL INDEXES way with RANGE partitioned tables or the
global index is left UNUSABLE

SQL> ALTER TABLE orders MERGE PARTITIONS q2, q3 INTO PARTITION q3


2 UPDATE GLOBAL INDEXES;

Table altered.

2. HASH partitioned tables

No MERGE allowed with HASH partitioned tables.


Use COALESCE instead. (See at the end of the bulletin)

3. COMPOSITE partitioned tables

_______________________________________________________________________
Prepared by M.A.Asad Page 77 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Use UPDATE GLOBAL INDEXES way with COMPOSITE partitioned tables or


the global index is left UNUSABLE

SQL> ALTER TABLE emp_composite MERGE PARTITIONS p1, p2


2 INTO PARTITION p2
3 UPDATE GLOBAL INDEXES;

Table altered.

4. LIST partitioned tables:

Use UPDATE GLOBAL INDEXES way with LIST partitioned tables or the global
index is left UNUSABLE

SQL> alter table locations MERGE PARTITIONS region_east,region_west


2 INTO PARTITION region_north
3 UPDATE GLOBAL INDEXES;
Table altered.

UPDATE GLOBAL INDEXES clause and EXCHANGE PARTITION clause

1. RANGE partitioned tables

Use UPDATE GLOBAL INDEXES way with RANGE partitioned tables or the
global index is left UNUSABLE

SQL> ALTER TABLE orders EXCHANGE PARTITION q3 WITH TABLE t_orders


2 UPDATE GLOBAL INDEXES;
Table altered.

If GLOBAL indexes exist on the TABLE of exchange, they are left UNUSABLE:

SQL> create index t_orders_global_idx


2 on t_orders(ord_date)
3 global partition by range (ord_date)
4 (partition GLOBAL1 values less than (TO_DATE('01-APR-1999','DD-MON-YYYY',
5 partition GLOBAL2 values less than (TO_DATE('01-SEP-1999','DD-MON-YYYY',
6 partition GLOBAL3 values less than (TO_DATE('01-DEC-1999','DD-MON-YYYY',
7 partition GLOBAL4 values less than (MAXVALUE) );

Index created.

SQL> ALTER TABLE orders EXCHANGE PARTITION q3 WITH TABLE t_orders


2 UPDATE GLOBAL INDEXES;

Table altered.

SQL> @sel

_______________________________________________________________________
Prepared by M.A.Asad Page 78 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
INDEX_NAME PART_NAME STATUS
-------------------- -------------------- --------
T_ORDERS_GLOBAL_IDX GLOBAL1 UNUSABLE
T_ORDERS_GLOBAL_IDX GLOBAL2 UNUSABLE
T_ORDERS_GLOBAL_IDX GLOBAL3 UNUSABLE
T_ORDERS_GLOBAL_IDX GLOBAL4 UNUSABLE
ORDERS_GLOBAL_IDX GLOBAL1 USABLE
ORDERS_GLOBAL_IDX GLOBAL2 USABLE
ORDERS_GLOBAL_IDX GLOBAL3 USABLE
ORDERS_GLOBAL_IDX GLOBAL4 USABLE

2. HASH partitioned tables

Use UPDATE GLOBAL INDEXES way with HASH partitioned tables or the global
index is left UNUSABLE

SQL> ALTER TABLE emp_hpart EXCHANGE PARTITION H1 WITH TABLE


t_emp_hpart UPDATE GLOBAL INDEXES;

Table altered.

3. COMPOSITE partitioned tables

SQL> ALTER TABLE emp_composite EXCHANGE PARTITION p1 WITH TABLE


t_emp_composite;
ALTER TABLE emp_composite EXCHANGE PARTITION p1 WITH TABLE
t_emp_composite
*
ERROR at line 1:
ORA-14291: cannot EXCHANGE a composite partition with a non-partitioned table

Use UPDATE GLOBAL INDEXES way with COMPOSITE partitioned tables or the
global index is left UNUSABLE

SQL> ALTER TABLE emp_composite EXCHANGE SUBPARTITION SYS_SUBP286


2 WITH TABLE t_emp_composite
3 UPDATE GLOBAL INDEXES;
Table altered.

4. LIST partitioned tables:

Use UPDATE GLOBAL INDEXES way with LIST partitioned tables or the global index
is left UNUSABLE

SQL> ALTER TABLE locations EXCHANGE PARTITION region_east


2 WITH TABLE t_locations
3 UPDATE GLOBAL INDEXES;

_______________________________________________________________________
Prepared by M.A.Asad Page 79 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Table altered.

UPDATE GLOBAL INDEXES clause and MOVE PARTITION clause

1. RANGE partitioned tables

Use UPDATE GLOBAL INDEXES way with RANGE partitioned tables or the global
index is left UNUSABLE

SQL> ALTER TABLE orders MOVE PARTITION q3 TABLESPACE example


2 UPDATE GLOBAL INDEXES;
Table altered.

2. HASH partitioned tables

Use UPDATE GLOBAL INDEXES way with HASH partitioned tables or the global
index is left UNUSABLE |

SQL> ALTER TABLE emp_hpart MOVE PARTITION H1 TABLESPACE example


2 UPDATE GLOBAL INDEXES;
Table altered.

3. COMPOSITE partitioned tables

SQL> ALTER TABLE emp_composite MOVE PARTITION p1 TABLESPACE


example;
ALTER TABLE emp_composite MOVE PARTITION p1 TABLESPACE example
*
ERROR at line 1:
ORA-14257: cannot move partition other than a Range or Hash partition

SQL> ALTER TABLE emp_composite MOVE PARTITION p1 TABLESPACE


example
2 UPDATE GLOBAL INDEXES;
ALTER TABLE emp_composite MOVE PARTITION p1 TABLESPACE example
*
ERROR at line 1:
ORA-14257: cannot move partition other than a Range or Hash partition

4. LIST partitioned tables:


Use UPDATE GLOBAL INDEXES way with LIST partitioned tables or the global
index is left UNUSABLE |

SQL> ALTER TABLE locations MOVE PARTITION region_east


2 TABLESPACE TS_DATA1
3 UPDATE GLOBAL INDEXES;

_______________________________________________________________________
Prepared by M.A.Asad Page 80 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Table altered.

UPDATE GLOBAL INDEXES clause and TRUNCATE PARTITION clause

1. RANGE partitioned tables

Use UPDATE GLOBAL INDEXES way with RANGE partitioned tables or the global
index is left UNUSABLE

SQL> ALTER TABLE orders TRUNCATE PARTITION q3


2 UPDATE GLOBAL INDEXES;
Table truncated.

2. HASH partitioned tables

Use UPDATE GLOBAL INDEXES way with HASH partitioned tables or the global
index is left UNUSABLE

SQL> ALTER TABLE emp_hpart TRUNCATE PARTITION H1


2 UPDATE GLOBAL INDEXES;
Table truncated.

3. COMPOSITE partitioned tables

Use UPDATE GLOBAL INDEXES way with COMPOSITE partitioned tables or the
global index is left UNUSABLE

SQL> ALTER TABLE emp_composite TRUNCATE PARTITION p1


2 UPDATE GLOBAL INDEXES;
Table truncated.

4. LIST partitioned tables:

Use UPDATE GLOBAL INDEXES way with LIST partitioned tables or the global
index is left UNUSABLE

SQL> ALTER TABLE locations TRUNCATE PARTITION region_east


2 UPDATE GLOBAL INDEXES;

Table truncated.

UPDATE GLOBAL INDEXES clause and COALESCE PARTITION clause

_______________________________________________________________________
Prepared by M.A.Asad Page 81 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
1. RANGE partitioned tables:
Not available with range partitioned tables.

2. HASH partitioned tables:

Use UPDATE GLOBAL INDEXES way with HASH partitioned tables or the global
index is left UNUSABLE
SQL> ALTER TABLE emp_hpart COALESCE PARTITION
2 UPDATE GLOBAL INDEXES;
Table altered.

3. COMPOSITE partitioned tables


Not available with composite partitioned tables.

4. LIST partitioned tables


Not available with list partitioned tables.

Which performance considerations would lead you to choose REBUILD INDEX rather
than use UPDATE GLOBAL INDEXES?

If UPDATE GLOBAL INDEXES is used:

 Partition DDL operations will take longer to complete because the global indexes
that were previously marked invalid will now be updated
 DROP, TRUNCATE, EXCHANGE will no longer be fast, though data-dictionary
only operations because a scan of all rows in the partition will be done
 Updates to the global index will be logged, hence, redo and rollback will be
generated
 Update index is favorable when the amount of row work is low
 Update index ensures application performance does not drastically fall until the
index is rebuilt

If INDEX REBUILD is used:

 Rebuilding the entire index will make the index more efficient.
 Rebuilding the index allows the user to reorganize the index

8.1.6 Exchange Partition Enhancements

EXCHANGE PARTITION

8.0

In Oracle 8.0 The concept of partitioning was introduced, whereby a table could be
managed as a number of separate objects known as partitions. In a partitioned table, there
ceased to be a single physical segment to represent the table, and a 'table' becomes a
dictionary entity. The partitions hold the data and are managed as disk segments ( or
fragments). In order to convert existing tables to larger partitioned tables and to manage
_______________________________________________________________________
Prepared by M.A.Asad Page 82 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
rolling updates of large amounts of data, the EXCHANGE PARTITION command was
introduced.
This command is very powerful, since it can swap a standalone table with a partition in a
partitioned table with no data movement. The EXCHANGE partition command swaps
the dictionary information for the table and the partition, and therefore can be achieved
very rapidly, when done WITHOUT VALIDATION ( The kernel assumes that the user
has validated that the data is correctly partitioned beforehand )

8.1.5

With the introduction of Oracle 8i release 8.1.5 the concepts of Hash and Composite
( Range + Hash ) partitioning was introduced. This allowed a partitioned object to be
split in two dimensions where a Range partition can be split into Hash sub-partitions. In
Composite partitioning, the 'table' and its 'partitions' are all virtual objects, and the
fragments represent sub-partitions. The exchange command was extended to allow
exchange of sub-partitions with standard tables, since this was merely an extension of the
8.0 code.

The limitation however, was that it is not possible to exchange a whole Composite
partition, with a Hash partitioned table, even though the partition and its sub-partitions
are the same 'Shape' as the table. This could be achieved, however it involved a
complicated process of exchanging each partition out of the Hash partitioned table, and
then exchanging the resultant tables with the sub-partitions. This is extremely time
consuming if there are a large number of fragments to exchange.

8.1.6

In 8i release 8.1.6 It is now possible to EXCHANGE a complete Composite partition


( i.e. a Partition, its associated hash partitioned fragments, and optionally their
associated local indexes ) with a standalone hash partitioned table. This allows users to
build maintainable rolling updates on Composite partitioned tables, such that a large
amount of data can be built off-line in a Hash partitioned table, then quickly transferred
into the main composite partitioned table via DDL.

Restrictions and associated errors..........

Trying to exchange a normal Oracle table into a composite partition ........


ORA-14291: cannot EXCHANGE a composite partition with a non-partitioned table

Trying to exchange a range or composite partitioned table into a


Composite Partition.....
ORA-14292 : Partitioning type of table must match subpartitioning type of composite
partition

Trying to exchange a Hash partitioned table into a Composite partition where the number
of columns in the partition key is different.....
ORA-14293 : Number of partitioning columns does not match number of
subpartitioning columns

_______________________________________________________________________
Prepared by M.A.Asad Page 83 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Trying to Exchange a Hash partitioned table into a Composite partition where the
number of partitions is different....
ORA-14294: Number of partitions does not match number of sub-partitions

Trying to exchange a hash partitioned table into a composite partitioned table where the
partition key has the same number of columns, but is a different column....
ORA-14295 : Partitioning type of table must match subpartitioning type of composite
partition

Creating & Adding Table and Index Partitions

Oracle 8 allows for tables to be divided into different storage areas called partitions. Each
partition of a table will contain the same constraints, columns and datatypes as all other
partitions of that table, but can have different storage attributes i.e. each partition can be
stored in separate tablespaces on different disks.

Example :-

create table orders (


order_no number,
part_no varchar2(40),
ord_date date
)
partition by range (ord_date)
(partition Q1 values less than (TO_DATE('01-APR-1999','DD-MON-YYYY'))
tablespace Q1_DATA,
partition Q2 values less than (TO_DATE('01-JUL-1999','DD-MON-YYYY'))
tablespace Q2_DATA,
partition Q3 values less than (TO_DATE('01-OCT-1999','DD-MON-YYYY'))
tablespace Q3_DATA,
partition Q4 values less than (TO_DATE('01-JAN-2000','DD-MON-YYYY'))
tablespace Q4_DATA)
;

As you can see, no STORAGE values have been specified, in this example the partitions
will inherit the default storage attributes of the tablespaces. If you wish physical
attributes and the storage clause can be specified for the entire table :-

i.e.
create table orders (
order_no number,
part_no varchar2(40),
ord_date date
)
storage (initial 1M next 1M pctincrease 0)
partition by range (ord_date)
(partition Q1 values less than (TO_DATE('01-APR-1999','DD-MON-YYYY'))

_______________________________________________________________________
Prepared by M.A.Asad Page 84 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
tablespace Q1_DATA,
partition Q2 values less than (TO_DATE('01-JUL-1999','DD-MON-YYYY'))
tablespace Q2_DATA,
partition Q3 values less than (TO_DATE('01-OCT-1999','DD-MON-YYYY'))
tablespace Q3_DATA,
partition Q4 values less than (TO_DATE('01-JAN-2000','DD-MON-YYYY'))
tablespace Q4_DATA)
;

or the attributes can be specified at the partition level :-

i.e.
create table orders (
order_no number,
part_no varchar2(40),
ord_date date
)
partition by range (ord_date)
(partition Q1 values less than (TO_DATE('01-APR-1999','DD-MON-YYYY'))
storage (initial 1M next 1M pctincrease 0)
tablespace Q1_DATA,
partition Q2 values less than (TO_DATE('01-JUL-1999','DD-MON-YYYY'))
storage (initial 2M next 1M pctincrease 0)
tablespace Q2_DATA,
partition Q3 values less than (TO_DATE('01-OCT-1999','DD-MON-YYYY'))
storage (initial 5M next 2M pctincrease 0)
tablespace Q3_DATA,
partition Q4 values less than (TO_DATE('01-JAN-2000','DD-MON-YYYY'))
storage (initial 2M next 5M pctincrease 0)
tablespace Q4_DATA)
;

As well as having the table partitioned, the associated indexes can also be partitioned.
Like the tables, the index partitions must have the same logical attributes i.e. index
columns, but can have different storage attributes i.e. each partition can again be stored
in different locations on the server.

There are two main partitioned index types, global and local:-

GLOBAL
The global index type allows the index partitions to be different from the underlying
partitioned table or if you wish they can be the same. Using global indexes means that
the index does not have a direct relationship with the table, unlike the local index type
which is discussed below. Using the above partitioned table example we can create the
global index with or without the same partition attributes.

With the same attributes:

create index orders_global_1_idx


on orders(ord_date)

_______________________________________________________________________
Prepared by M.A.Asad Page 85 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
global partition by range (ord_date)
(partition GLOBAL1 values less than (TO_DATE('01-APR-1999','DD-MON-YYYY'))
tablespace Q1_INDEX,
partition GLOBAL2 values less than (TO_DATE('01-JUL-1999','DD-MON-YYYY'))
tablespace Q2_INDEX,
partition GLOBAL3 values less than (TO_DATE('01-OCT-1999','DD-MON-YYYY'))
tablespace Q3_INDEX,
partition GLOBAL4 values less than (MAXVALUE)
tablespace Q4_INDEX)
;

With different partition attributes:

create index orders_global_2_idx


on orders(ord_date)
global partition by range (ord_date)
(partition INDEX1 values less than (TO_DATE('01-JUL-1999','DD-MON-YYYY'))
tablespace INDEX1,
partition INDEX2 values less than (MAXVALUE)
tablespace INDEX2)
;

With different partition attributes and key:

create index orders_global_3_idx


on orders(part_no)
global partition by range (part_no)
(partition INDEX1 values less than (555555)
tablespace INDEX1,
partition INDEX2 values less than (MAXVALUE)
tablespace INDEX2)
;

As can be seen, as the indexes are Global and do not have a direct relationship with the
table, the option of MAXVALUE must be specified. The index will not have a new
partition added automatically if the table has a new partition added.

With Global indexes the index key must be the same as the partition key, this is known as
a prefixed index (where the leftmost column in the index matches the leftmost column in
the index's partition key). If you wish the index key to be different to the partition key i.e.
a non prefixed index, then the index must be created a Local index or you will receive an
'ORA-14038 Global partitioned index must be prefixed'.

LOCAL

With local indexes the partition ranges are not specified as the index is local to the table.
Therefore when a Local index is created Oracle will actually create a separate index for
each partition in the table.

create index orders_local_1_idx

_______________________________________________________________________
Prepared by M.A.Asad Page 86 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
on orders(ord_date)
local
(partition LOCAL1
tablespace Q1_INDEX,
partition LOCAL2
tablespace Q2_INDEX,
partition LOCAL3
tablespace Q3_INDEX,
partition LOCAL4
tablespace Q4_INDEX)
;

The option of MAXVALUE doesn't need to be specified because if a new table partition
is added, then an new index partition will be added automatically. We will discuss the
attributes and location of the new index partitions shortly. The above index creation is a
prefixed index as with the Global examples, as was mentioned previously Local indexes
can also be non prefixed.

create index orders_local_2_idx


on orders(part_no)
local
(partition LOCAL1
tablespace Q1_INDEX,
partition LOCAL2
tablespace Q2_INDEX,
partition LOCAL3
tablespace Q3_INDEX,
partition LOCAL4
tablespace Q4_INDEX)
;

Here our index key is part_no but our partition key is the same as the table
i.e. ord_date.

If we now add a new partition to our orders table:-

alter table orders


add partition NEW_PART values less than (TO_DATE('01-APR-2000','DD-MON-
YYYY'))
tablespace new_tab_part
;
there will automatically be an index partition created for our local index

select substr(index_name,1,20) index_name, substr(tablespace_name,1,20)


tablespace_name, substr(partition_name,1,20) part_name
from dba_ind_partitions
where index_name = 'ORDERS_LOCAL_2_IDX'
order by partition_name
;

_______________________________________________________________________
Prepared by M.A.Asad Page 87 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
INDEX_NAME TABLESPACE_NAME PART_NAME
-------------------- -------------------- --------------------
ORDERS_LOCAL_2_IDX Q1_INDEX LOCAL1
ORDERS_LOCAL_2_IDX Q2_INDEX LOCAL2
ORDERS_LOCAL_2_IDX Q3_INDEX LOCAL3
ORDERS_LOCAL_2_IDX Q4_INDEX LOCAL4
ORDERS_LOCAL_2_IDX NEW_TAB_PART NEW_PART

When we created the index initially we could specify where the index partitions are
located and what they are called. The same goes for creating the table partitions and
adding the new partitions. But, as a new local index partition is added automatically we
cannot specify where this is located or what it is called. As the above select shows the
new local index partition has to be put in the same tablespace as the new table partition
and has the same name.

There is a way of getting around this problem, as having the index and table partition
located in the same place is not ideal.

What we need to do to change the location of the new index partition is to change the
location of the default tablepspace for the index. If a default tablespace isn't set then the
index partition will be located in the same tablespace as the table partition, as in the
example above. But if we modify the default tablespace:-

alter index orders_local_2_idx


modify default attributes
tablespace new_ind_part
;

then once the table partition is added the new index partition will go into the
new_ind_part tablespace.

INDEX_NAME TABLESPACE_NAME PART_NAME


-------------------- -------------------- --------------------
ORDERS_LOCAL_2_IDX Q1_INDEX LOCAL1
ORDERS_LOCAL_2_IDX Q2_INDEX LOCAL2
ORDERS_LOCAL_2_IDX Q3_INDEX LOCAL3
ORDERS_LOCAL_2_IDX Q4_INDEX LOCAL4
ORDERS_LOCAL_2_IDX NEW_IND_PART NEW_PART

now we need to change the name of the partition, this can be done by :-

alter index orders_local_2_idx


rename partition NEW_PART to LOCAL5
;

Create Range Partition on Alphanumeric Column


You have a column in a table that is made up of alphanumeric data. Column datatype is
varchar2(xxx). Column contains data that are all numbers (384758) or all alpha
(ADBEDFE) or alphanumeric (AB343234).

_______________________________________________________________________
Prepared by M.A.Asad Page 88 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

You want to break the table into small partitions based upon a range. You do not know
how the alphanumeric data and the alpha data will be divided into the correct partition.

From UNIX prompt:

Issue a man on ascii


| 0 NUL| 1 SOH| 2 STX| 3 ETX| 4 EOT| 5 ENQ| 6 ACK| 7 BEL|
| 8 BS | 9 HT | 10 NL | 11 VT | 12 NP | 13 CR | 14 SO | 15 SI |
| 16 DLE| 17 DC1| 18 DC2| 19 DC3| 20 DC4| 21 NAK| 22 SYN| 23 ETB|
| 24 CAN| 25 EM | 26 SUB| 27 ESC| 28 FS | 29 GS | 30 RS | 31 US |
| 32 SP | 33 ! | 34 " | 35 # | 36 $ | 37 % | 38 & | 39 ' |
| 40 ( | 41 ) | 42* | 43 + | 44 , | 45 - | 46 . | 47 / |
| 48 0 | 49 1 | 50 2 | 51 3 | 52 4 | 53 5 | 54 6 | 55 7 |
| 56 8 | 57 9 | 58 : | 59 ; | 60 < | 61 = | 62 > | 63 ? |
| 64 @ | 65 A | 66 B | 67 C | 68 D | 69 E | 70 F | 71 G |
| 72 H | 73 I | 74 J | 75 K | 76 L | 77 M | 78 N | 79 O |
| 80 P | 81 Q | 82 R | 83 S | 84 T | 85 U | 86 V | 87 W |
| 88 X | 89 Y | 90 Z | 91 [ | 92 \ | 93 ] | 94 ^ | 95 _ |
| 96 ` | 97 a | 98 b | 99 c |100 d |101 e |102 f |103 g |
|104 h |105i |106 j |107 k |108 l |109 m |110 n |111 o |
|112 p |113 q |114 r |115 s |116 t |117 u |118 v |119 w |
|120 x |121 y |122 z |123 { |124 | |125 } |126 ~ |127 DEL|

From the above table, the numeric values evaluate less than capital letters, caps evaluate
less than lowercase and lowercase evaluate less than some special characters ({, |, }, etc.)

Decide how the partition should be divided and create the partitions by range. Values less
than 9, less than A, less than a, etc. Understand that values less than does not include
that value. So to include a range of numbers between 0-9 you must include 9 and have
values less than :, values less than B (will include values that start with A, and so on.

Example syntax:

create table <name>


(<column list, col list, etc.>)
storage (<clause>)
partition by range (<alphanumeric col>)
(partition pA values less than (a)
tablespace <tablespace for partition>,
partition pB values less than (b)
tablespace <tablespace for partition>,
...
partition pEnd values less than (~);

Depending on how the alpha characters in the table evaluate, using upper or lower case
letters should place the data into the proper partition. To further distinguish the
partitions, use 2 or 3 values for the partition clause.

_______________________________________________________________________
Prepared by M.A.Asad Page 89 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
partition pB values less than (bc) or partition pB values less than (bcd)

Explanation

All ASCII characters have a specific numeric value from 0-127. Each partition will have
an upper value for it based upon the how the ascii value that each letter or number in the
alphanumeric string evaluates.

The further defined you want the partition the more values you add into the evaluation
clause when creating the partition.

Partition Views and the use of Indexes (7.1 & 7.2)


This bulletin discusses changes in Oracle7, version 7.2, that will enable, subject to
certain conditions,indexes to be used with views containing Union All predicates. If the
conditions listed in this bulletin are satisfied and the cost of this path is favorable, then
tables within the Union All view should be accessed using available indexes. Currently
views containing certain operands (e.g. Union All) cannot be merged into the main query,
and thus either indexes on the tables in the views may not be used, or may be used in a
manner that causes the queries to perform less than optimally. The changes made only
relate to the Cost Based Optimizer.

Background

When the Oracle7 optimizer encounters a view in the from clause of a query, it attempts
to 'merge' this into the main query. This is achieved by transforming the view into
equivalent sql statements on the base tables. By transforming views into equivalent base
table joins, the optimizer does not require special code to handle view joins, and Oracle
can use the standard join algorithms.

However, with some views, it is not possible to create equivalent statements that access
only the base tables of the view and so the optimizer is unable to transform these
statements. In these cases, the optimizer issues the view's query and then uses the
resulting row source to join to the other tables.
This may result in indexes not being used. The ORACLE7 Concepts manual explains
this in more detail.

This behavior often leads to confusion, as when the view is executed separately indexes
are used and performance is as expected.

Note that this behavior is only exhibited by join rather than constant predicates.
Predicates involving constants such as deptno=5, are pushed into the view and can be
used as index drivers.

Take the following example from Oracle7 version 7.1:

1. Create 2 tables with identical schemas:

CREATE TABLE e1 AS SELECT * FROM emp;

_______________________________________________________________________
Prepared by M.A.Asad Page 90 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
8999 additional rows were then added to table e1.
These additional rows were spread over 100 departments. The additional rows modified
the statistics sufficiently that an index scan may be preferable to a Full Table Scan.

CREATE TABLE e2 AS SELECT * FROM emp;

No additional rows were added to e2 so that comparisons could be made.

2. Create identical indexes on the new tables.

CREATE INDEX i_e1 FOR e1(deptno);


CREATE INDEX i_e2 FOR e2(deptno);

3. Analyze the tables for use by the Cost Based Optimizer:

ANALYZE TABLE dept COMPUTE STATISTICS;


ANALYZE TABLE e1 COMPUTE STATISTICS;
ANALYZE TABLE e2 COMPUTE STATISTICS;

4. Create a view containing a union all predicate:

CREATE OR REPLACE VIEW emp_view AS


(SELECT *
FROM e1
UNION ALL
SELECT *
FROM e2);

5. The following select statements were 'explained' to show the relative costs of the
various underlying operations:

SELECT * FROM dept;

Execution Plan
---------------------------------------------------
SELECT STATEMENT Cost = 1
TABLE ACCESS FULL DEPT

SELECT * FROM e1;


Execution Plan
---------------------------------------------------
SELECT STATEMENT Cost = 51
TABLE ACCESS FULL E1

SELECT * FROM e1 WHERE deptno=10;


Execution Plan
---------------------------------------------------
SELECT STATEMENT Cost = 6
TABLE ACCESS BY ROWID E1
INDEX RANGE SCAN I_E1 (NON-UNIQUE)

_______________________________________________________________________
Prepared by M.A.Asad Page 91 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

SELECT * FROM e2 WHERE deptno=10;


Execution Plan
---------------------------------------------------
SELECT STATEMENT Cost = 1
TABLE ACCESS FULL E2

SELECT * FROM emp_view WHERE deptno=10;

Execution Plan
---------------------------------------------------
SELECT STATEMENT Cost = 17
VIEW EMP_VIEW
PROJECTION
UNION-ALL
TABLE ACCESS BY ROWID E1
INDEX RANGE SCAN I_E1 (NON-UNIQUE)
TABLE ACCESS FULL E2

When the EMP_VIEW view is queried alone, the index on E1 (9013 rows - 100
departments) is used. This is because the cost of the index lookup (6) is significantly less
than the cost of a Full Table Scan (51).

The index on E2 is not used as the table only contains 14 rows and the cost of a Full
Table Scan is less than retrieving the rows using an index in this case.

View merging is only required when a view is joined to another table.

View transformation is not possible if the view's query contains any of the following:

- set operators (union, union all, intersect, minus)


- group by
- connect by
- distinct
- group functions (avg,count,max,min,sum)

This list is documented on Page 13-22 of the ORACLE7 Concepts Manual.

If the EMP_VIEW view above is joined to the DEPT table, then the optimizer is unable
to merge the view into the query because of the union all predicate in the view. This
results in a suboptimal execution plan:

SELECT d.dname, e.ename


FROM dept d,
emp_view e
WHERE d.deptno = e.deptno
AND d.dname = 'ACCOUNTING';

Execution Plan

_______________________________________________________________________
Prepared by M.A.Asad Page 92 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
---------------------------------------------------
SELECT STATEMENT Cost = 316
NESTED LOOPS
TABLE ACCESS FULL DEPT
VIEW EMP_VIEW
PROJECTION
UNION-ALL
TABLE ACCESS FULL E1
TABLE ACCESS FULL E2

In this case the optimizer is forced to do a Full Table Scan of E1 even though an index
scan would be less costly IF the deptno for the 'ACCOUNTING' department could be
passed into the view.

The reason that this predicate is not pushed into the view is because the view and the
query it joins to are treated as totally separate query blocks. Predicates cannot be passed
between query blocks because in most cases this can result in incorrect results. The only
construct where we allow predicates to be pushed into the view is where the view has
been rigidly defined and conforms to the rules required to make it a partition view. If
these criteria are not met, we must select from the view and the outer tables separately
and merge the results together.

It is possible to force the use of an index by including an index hint in the view
definition:

CREATE OR REPLACE VIEW emp_view1 AS


(SELECT /*+ INDEX(e1) */ *
FROM e1
UNION ALL
SELECT /*+ INDEX(e2) */ *
FROM e2);

In this case, the path chosen results in a full scan of the index being performed rather
than an index range scan. This is because the optimizer cannot merge the view, and thus
use an index, but within the view the optimizer has been told to use the index.

Rows Execution Plan


------ ---------------------------------------------------
0 SELECT STATEMENT Cost = 603
6 NESTED LOOPS
100 TABLE ACCESS FULL DEPT
9027 VIEW EMP_VIEW1
9027 PROJECTION
9027 UNION-ALL
9013 TABLE ACCESS BY ROWID E1
9014 INDEX RANGE SCAN I_E1 (NON-UNIQUE)
14 TABLE ACCESS BY ROWID E2
15 INDEX RANGE SCAN I_E2 (NON-UNIQUE)

_______________________________________________________________________
Prepared by M.A.Asad Page 93 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
From the explain plan above the rows retrieved from the INDEX RANGE SCAN of I_E1
clearly shows that a full index scan is being performed.

When this is compared with the query required to retrieve the relevant data from table e1
the difference is clear.

SELECT ename FROM e1 WHERE deptno=10;

Rows Execution Plan


------- ---------------------------------------------------
0 SELECT STATEMENT Cost = 6
3 TABLE ACCESS BY ROWID E1
4 INDEX RANGE SCAN I_E1 (NON-UNIQUE)

In other words, even though it is now using an index, it is reading all of the
index as opposed to a small portion of it. It does not apply the predicates from the table
that is joined to the non-mergeable view. So using the index may not really help and
may in fact be slower.

Changes in Oracle7 Version 7.2

In ORACLE7 version 7.2, it is possible to get a Union All view to use indexes more
efficiently if the view is deemed to be selecting from Manual Partitions.

A manual partition is where a single logical table has been split into multiple physical
tables with the same columns, each storing a range of the data. This practice is
commonplace when dealing with very large datasets such as in Data Warehousing
applications. Union All provides the mechanism to group data so that it seems to come
from one source.

The use of Manual Partitions is subject to a number of strict restrictions that must be
satisfied for a view to be termed a Manual Partition.

1. Tables joined by the Union All must be identical in schema.

2. Each branch of the view that is unioned together must contain only 1 table in the from
clause.

3. The query must not use any of the following constructs:

a. group-by, aggregate functions, distinct


b. rownum, start-with/connect-by
c. where-clause

4. Indexes on the tables must be identical.

5. Selects in the view must either:

a. select * from the table;


b. Explicitly select all columns in the same order as they exist in

_______________________________________________________________________
Prepared by M.A.Asad Page 94 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
the table.

The code change enables parameters to be passed into the view definition so that indexes
can be used rather than merging the view into the outer query.

If these conditions are not satisfied, the indexes will not be used.

Take the following example from Oracle7 version 7.2:

CREATE OR REPLACE VIEW emp_view3 AS


SELECT deptno, ename
FROM e1
UNION ALL
SELECT deptno, ename
FROM e2)

This does not satisfy rule 3b; it does not select all the columns.

SELECT d.dname, e.ename


FROM dept d, emp_view3 e
WHERE d.deptno = e.deptno
AND d.dname = 'ACCOUNTING';

Rows Execution Plan


------- ---------------------------------------------------
0 SELECT STATEMENT Cost = 36
7 NESTED LOOPS
100 TABLE ACCESS FULL DEPT
9027 VIEW EMP_VIEW3
9027 PROJECTION
9027 UNION-ALL
9013 TABLE ACCESS FULL E1
14 TABLE ACCESS FULL E2

The view cannot be merged and a Full Table Scan is forced.

However, selecting from a view that meets the conditions gives such as the EMP_VIEW
view defined earlier in this bulletin:

SELECT d.dname, e.ename


FROM dept d, emp_view e
WHERE d.deptno = e.deptno
AND d.dname = 'ACCOUNTING';

Rows Execution Plan


------- ---------------------------------------------------
0 SELECT STATEMENT Cost = 5
7 NESTED LOOPS
100 TABLE ACCESS FULL OF DEPT
7 VIEW OF EMP_VIEW

_______________________________________________________________________
Prepared by M.A.Asad Page 95 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
7 PROJECTION
7 UNION-ALL
4 TABLE ACCESS BY ROWID OF E1
5 INDEX RANGE SCAN OF I_E1 (NON-UNIQUE)
14 TABLE ACCESS FULL OF E2

If you are unable to meet the conditions above, then the workaround (as in
Oracle7 version 7.1 and 7.0) is to recode the query so that it does not involve
a view.

For example:

SELECT d.dname, e.ename


FROM dept d, e1 e
WHERE d.deptno = e.deptno
AND d.dname = 'ACCOUNTING'
UNION ALL
SELECT d.dname, e.ename
FROM dept d, e2 e
WHERE d.deptno = e.deptno
AND d.dname = 'ACCOUNTING'

Execution Plan
---------------------------------------------------
SELECT STATEMENT Cost = 7
PROJECTION
UNION-ALL
NESTED LOOPS
TABLE ACCESS FULL DEPT
TABLE ACCESS BY ROWID E1
INDEX RANGE SCAN I_E1 (NON-UNIQUE)
NESTED LOOPS
TABLE ACCESS FULL DEPT
TABLE ACCESS BY ROWID E2
INDEX RANGE SCAN I_E2 (NON-UNIQUE)

--------------------------------------------------------------------------

Privileges required to split a partitioned Index organized table

How to split PIOT from another schema

For some reasons you want to split your partitioned Index organized table (PIOT) from
another schema, which is not the owner of the PIOT and also is not a DBA.

You have created a user, for example, scott and created a partitioned index organized
table under it.

_______________________________________________________________________
Prepared by M.A.Asad Page 96 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
> CREATE TABLE bm (STEPNUMBER NUMBER(10,0),DATAAREAID
VARCHAR2(3),
CONSTRAINT PK_BM PRIMARY KEY (DATAAREAID,STEPNUMBER))
ORGANIZATION INDEX
PARTITION BY RANGE (dataareaid) (PARTITION AXPAR_DAT VALUES LESS
THAN
('dau') ) ;

You have now created a separate user who will be splitting this PIOT.

> conn / as sysdba

> create user test identified by test default tablespace users temporary
tablespace temp;

> grant connect, resource to test;

You know user "test" cannot split partition as it has no privileges on object "BM" . So
you grant all object level privileges to user "test"

> conn scott/tiger


> grant all on bm to test;

You are now attempting to split partition from user test.

> conn test/test


> ALTER TABLE scott.BM SPLIT PARTITION AXPAR_DAT AT ('abx') INTO
(PARTITION
AXPAR_ABW , PARTITION AXPAR_DAT );

The error you will get then depends on which version you are.

If you are on 8.1.7 you will get something like this:

ERROR at line 1:
ORA-00600: internal error code, arguments: [604], [ctcicttb], [], [], [], [],
[], []

for 9.0.1 and 9.2.0, you get:

ERROR at line 1:
ORA-01031: insufficient privileges

There is no need to panic if you see ORA-00600 [604], [ctcicttb] in your database. The
[604] argument in the ORA-600 means that an ORA-604 was signalled during the
creation of the new partition. The ORA-600 [604] is not very helpful in determining the
cause of the error, because it merely says that an error was reported while executing a
recursive SQL statement.

_______________________________________________________________________
Prepared by M.A.Asad Page 97 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
That error doesn't show up in the ORA-600 trace file however. To find the error that was
signalled by the recursive sql, set an errorstack event on 604 error, like this:

> alter session set events '604 trace name errorstack level 3';

Before executing the SPLIT order again.

The trace file generated will then show something like that:

ksedmp: internal or fatal error


ORA-00604: Une erreur s'est produite au niveau SQL récursif 1
ORA-01031: privilèges insuffisants
Current SQL statement for this session:
ALTER TABLE scott.BM SPLIT PARTITION AXPAR_DAT AT ('abx')
INTO (PARTITION AXPAR_ABW , PARTITION AXPAR_DAT )

Which means you are still lacking some privileges.

What Privileges are required ?


There are two ways to handle this situation:

1. Run the split partition statement from the owning schema or from user who is granted
DBA privileges.

or

2. Grant the following privileges to the other schema:

System privileges:
CREATE SESSION
ALTER SESSION
UNLIMITED TABLESPACE
CREATE TABLE
CREATE ANY TABLE
ALTER ANY TABLE
DROP ANY TABLE
SELECT ANY TABLE
CREATE ANY INDEX

The initial list of system privileges obtained with CONNECT, RESOURCE roles are:

SQL> select * from session_privs


2 order by PRIVILEGE;

PRIVILEGE
----------------------------------------
ALTER SESSION
CREATE CLUSTER
CREATE DATABASE LINK
CREATE INDEXTYPE

_______________________________________________________________________
Prepared by M.A.Asad Page 98 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
CREATE OPERATOR
CREATE PROCEDURE
CREATE SEQUENCE
CREATE SESSION
CREATE SYNONYM
CREATE TABLE
CREATE TRIGGER
CREATE TYPE
CREATE VIEW
UNLIMITED TABLESPACE

14 rows selected.

If we add those not already present:

SQL> connect scott/tiger

SQL> grant create any table to test;


SQL> grant alter any table to test;
SQL> grant create any index to test;
SQL> grant select any table to test;
SQL> grant drop any table to test;

SQL> connect test/test

SQL> select * from session_privs


2 order by PRIVILEGE;

PRIVILEGE
----------------------------------------
ALTER ANY TABLE
ALTER SESSION
CREATE ANY INDEX
CREATE ANY TABLE
CREATE CLUSTER
CREATE DATABASE LINK
CREATE INDEXTYPE
CREATE OPERATOR
CREATE PROCEDURE
CREATE SEQUENCE
CREATE SESSION
CREATE SYNONYM
CREATE TABLE
CREATE TRIGGER
CREATE TYPE
CREATE VIEW
DROP ANY TABLE
SELECT ANY TABLE
UNLIMITED TABLESPACE

_______________________________________________________________________
Prepared by M.A.Asad Page 99 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
19 rows selected.

If you retry the SPLIT command now:

SQL> ALTER TABLE scott.BM SPLIT PARTITION AXPAR_DAT AT ('abx') INTO


(PARTITION
AXPAR_ABW , PARTITION AXPAR_DAT );

Table altered.

SQL> select table_owner, table_name, partition_name, partition_position from


dba_tab_partitions
where table_name = 'BM' and table_owner = 'SCOTT'
order by partition_position;

TABLE_OWNER TABLE_NAME PARTITION_NAME


PARTITION_POSITION
--------------- -------------------- -------------------- ------------------
SCOTT BM AXPAR_ABW 1
SCOTT BM AXPAR_DAT 2

partition operations,

for ALTER TABLE ... statement:

"
Additional Prerequisites for Partitioning Operations

If you are not the owner of the table, then you need the DROP ANY TABLE privilege in
order to use the drop_table_partition or truncate_table_partition clause.

You must also have space quota in the tablespace in which space is to be acquired in
order to use the add_table_partition, modify_table_partition, move_table_partition, and
split_table_partition clauses.
"

Summing up all the privileges required for splitting PIOT and those required for "alter
table", you will require above mentioned privileges.

How to compress local indexes on a Partition – Example

HOW TO COMPRESS LOCAL INDEX ON A PARTITION


A local index created on a partition will not allow users to compress it with a normal
ALTER INDEX....REBUILD COMPRESS statement.

alter index <index_name> rebuild compress;

If you try to compress the index using the above, Oracle will give the following error.

_______________________________________________________________________
Prepared by M.A.Asad Page 100 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

ORA-14086: a partitioned index may not be rebuilt as a whole

alter index <index_name> rebuild compress;

To compress a local index on partition, The data from the partition is to be exchanged to
a table which has a pre created index on the table using the ALTER TABLE
<table_name> EXCHANGE PARTITION statement.

Then compress the index on the table and then exchange the data from the table to
partition.
Now you have the partition with compressed data.

Here is an example workflow of compressing the local indexes on partitions.

1.create table COMP_TEST....


==========================
CREATE TABLE COMP_TEST (
INDEX_BILL_REF NUMBER (10) NOT NULL,
DISCOUNT NUMBER (18) NOT NULL
)
PARTITION BY RANGE (INDEX_BILL_REF)
(
partition pt1 values less than (10000)
STORAGE (
INITIAL 2k
NEXT 1k
PCTINCREASE 0
MINEXTENTS 1
MAXEXTENTS 100
),
partition pt2 values less than (20000)
STORAGE (
INITIAL 2k
NEXT 1k
PCTINCREASE 0
MINEXTENTS 1
MAXEXTENTS 100
)
);

2.create local index on the table COMP_TEST


===========================================
CREATE INDEX COMP_TEST_IND on COMP_TEST(DISCOUNT) local nologging;

3.create table EXCH


===================
create table exch as select * from COMP_TEST
where 1=2;

_______________________________________________________________________
Prepared by M.A.Asad Page 101 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
4.create index on table EXCH
============================
create index EXCH_IND on exch (discount);

5.Check the "COMPRESS" status for both Indexes from DBA_INDEXES


===============================================================
select index_name,compression from dba_indexes
where index_name in ('COMP_TEST_IND','EXCH_IND');
.
INDEX_NAME COMPRESS
------------------------------ --------
COMP_TEST_IND DISABLED
EXCH_IND DISABLED

6.Check the "COMPRESS" status for COMP_TEST_IND from


DBA_IND_PARTITIONS
select index_name,partition_name,compression,status
from dba_ind_partitions where index_name = 'COMP_TEST_IND';
.
INDEX_NAME PARTITION_NAME COMPRESS STATUS
---------- -------------- --------- --------
COMP_TEST_IND PT2 DISABLED USABLE
COMP_TEST_IND PT1 DISABLED USABLE
.

7.Verify the size of the each Partition and Indexes


====================================================
select segment_name,partition_name,bytes/(1048576) MB from dba_segments
where segment_name in ('COMP_TEST_IND','EXCH_IND');
.
SEGMENT_NAME PARTITION_NAME MB
------------ -------------- --
EXCH_IND .0078125
COMP_TEST_IND PT2 .1640625
COMP_TEST_IND PT1 .1640625
.

8.Now exchange the data from Partition PT2 to table EXCH.


=========================================================
alter table COMP_TEST exchange partition PT2 with table exch
including indexes with validation;
.
Now you have the data in the table EXCH.
.
select segment_name,partition_name,bytes/(1048576) MB from dba_segments
where segment_name in ('COMP_TEST_IND','EXCH_IND');

SEGMENT_NAME PARTITION_NAME MB
------------ -------------- --
EXCH_IND .1640625

_______________________________________________________________________
Prepared by M.A.Asad Page 102 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
COMP_TEST_IND PT2 .0078125
COMP_TEST_IND PT1 .1640625

9. Now compress the index on the table EXCH


===========================================
alter index exch_ind rebuild compress;
Index altered.
.
select segment_name,partition_name,bytes/(1048576) MB from dba_segments
where segment_name in ('COMP_TEST_IND','EXCH_IND');
.
SEGMENT_NAME PARTITION_NAME MB
------------ -------------- --
EXCH_IND .09375
COMP_TEST_IND PT2 .0078125
COMP_TEST_IND PT1 .1640625

10.Now exchange the data from table EXCH to Partition PT2


=========================================================

alter table COMP_TEST exchange partition PT2 with table EXCH


including indexes with validation;
.
Table altered.
.
select segment_name,partition_name,bytes/(1048576) MB from dba_segments
where segment_name in ('COMP_TEST_IND','EXCH_IND');
.

SEGMENT_NAME PARTITION_NAME MB
------------ -------------- --
EXCH_IND .0078125

COMP_TEST_IND PT2 .09375

COMP_TEST_IND PT1 .1640625


.
Now the index on partiton PT2 is compressed.

Before compression the size of the index was .01640625 MB(172032 bytes), and after
compression the size of the index is .09375 MB(98304 bytes).

How to partition a non-partitioned table.

You have a table that is not partitioned that you would like to make into a partitioned
table. This article describes three possible methods for partitioning a non-partitioned
table.

You can partition a non-partitioned table three different ways:

_______________________________________________________________________
Prepared by M.A.Asad Page 103 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

A) export/import method

B) Insert with a subquery method

C) Partition exchange method

Either of these 3 methods will create a partitioned table from an existing non-partitioned
table.

A. Export/import method

1) Export your table:

exp usr/pswd tables=numbers file=exp.dmp

2) Drop the table:

drop table numbers;

3) Recreate the table with partitions:

create table numbers (qty number(3), name varchar2(15))


partition by range (qty)
(partition p1 values less than (501),
partition p2 values less than (maxvalue));

4) Import the table with ignore=y:

imp usr/pswd file=exp.dmp ignore=y

The ignore=y causes the import to skip the table creation and
continues to load all rows.

B. Insert with a subquery method

1) Create a partitioned table:

create table partbl (qty number(3), name varchar2(15))


partition by range (qty)
(partition p1 values less than (501),
partition p2 values less than (maxvalue));

2) Insert into the partitioned table with a subquery from the


non-partitioned table:

insert into partbl (qty, name)


select * from origtbl;

_______________________________________________________________________
Prepared by M.A.Asad Page 104 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
3) If you want the partitioned table to have the same name as the original table, then
drop the original table and rename the new table:

drop table origtbl;


alter table partbl rename to origtbl;

C. Partition Exchange method

ALTER TABLE EXCHANGE PARTITION can be used to convert a partition (or


subpartition) into a non-partitioned table and a non-partitioned table into a partition (or
subpartition) of a partitioned table by exchanging their data and index segments.

1) Create table dummy_t as select with the required partitions

2) Alter table EXCHANGE partition <partition_name>


with <non_partition_table_name>;

Example

SQL> CREATE TABLE p_emp


2 (sal NUMBER(7,2))
3 PARTITION BY RANGE(sal)
4 (partition emp_p1 VALUES LESS THAN (2000),
5 partition emp_p2 VALUES LESS THAN (4000));
Table created.

SQL> SELECT * FROM emp;


EMPNO ENAME JOB MGR HIREDATE SAL
--------- ---------- --------- --------- --------- ---------
7369 SMITH CLERK 7902 17-DEC-80 800
7499 ALLEN SALESMAN 7698 20-FEB-81 1600
7521 WARD SALESMAN 7698 22-FEB-81 1250
7566 JONES MANAGER 7839 02-APR-81 2975
7654 MARTIN SALESMAN 7698 28-SEP-81 1250
7698 BLAKE MANAGER 7839 01-MAY-81 2850
7782 CLARK MANAGER 7839 09-JUN-81 2450
7788 SCOTT ANALYST 7566 19-APR-87 3000
7839 KING PRESIDENT 17-NOV-81 5000
7844 TURNER SALESMAN 7698 08-SEP-81 1500
7876 ADAMS CLERK 7788 23-MAY-87 1100
7900 JAMES CLERK 7698 03-DEC-81 950
7902 FORD ANALYST 7566 03-DEC-81 3000
7934 MILLER CLERK 7782 23-JAN-82 1300
14 rows selected.

SQL> CREATE TABLE dummy_y as SELECT sal


FROM emp WHERE sal<2000;
Table created.

_______________________________________________________________________
Prepared by M.A.Asad Page 105 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

SQL> CREATE TABLE dummy_z as SELECT sal FROM emp WHERE sal
BETWEEN 2000 AND 3999;
Table created.

SQL> alter table p_emp exchange partition emp_p1


with table dummy_y;
Table altered.

SQL> alter table p_emp exchange partition emp_p2


with table dummy_z;
Table altered.

How to Create Primary Key Partitioned Indexes

How To Create Primary Key Partitioned Indexes:

Example:

SQL> -- Create partitioned table TEST_A


SQL>
SQL> CREATE TABLE test_a (col1 number, col2 number, col3 varchar2(20))
2 PARTITION BY RANGE (col1, col2)
3 (partition part_test_a_1 values less than (10, 100),
4 partition part_test_a_2 values less than (20, 200),
5 partition part_test_a_3 values less than (30, 300),
6 partition part_test_a_4 values less than (40, 400));

Table created.

SQL> -- Create partitioned table TEST_B


SQL>
SQL> CREATE TABLE test_b (col1 number, col2 number, col3 varchar2(20))
2 PARTITION BY RANGE (col1, col2)
3 (partition part_test_b_1 values less than (10, 100),
4 partition part_test_b_2 values less than (20, 200),
5 partition part_test_b_3 values less than (30, 300),
6 partition part_test_b_4 values less than (40, 400));

Table created.

SQL> -- Create a non-unique local partitioned index, IX_TEST_A,


SQL> -- on TEST_A
SQL>
SQL> CREATE INDEX ix_test_a ON test_a(col1, col2)
2 LOCAL
3 (partition ix_test_a_1,

_______________________________________________________________________
Prepared by M.A.Asad Page 106 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
4 partition ix_test_a_2,
5 partition ix_test_a_3,
6 partition ix_test_a_4);

Index created.

SQL> -- Create a unique global partitioned index, IX_TEST_B,


SQL> -- on TEST_B
SQL>
SQL> CREATE UNIQUE INDEX ix_test_b1 ON test_b(col1, col2)
2 GLOBAL PARTITION BY RANGE (col1, col2)
3 (partition ix_test_b1_1 values less than (20, 200),
4 partition ix_test_b1_2 values less than (maxvalue, maxvalue));

Index created.

SQL> -- Add a primary key constraint, PK_TEST_A, to TEST_A


SQL>
SQL> ALTER TABLE test_a ADD CONSTRAINT pk_test_a
2 PRIMARY KEY (col2, col1);

Table altered.

SQL> -- Attempt to drop index IX_TEST_A; note the following error...


SQL>
SQL> DROP INDEX ix_test_a;
drop index ix_test_a
*
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key

SQL> -- Attempt to create a second index, IX_TEST_B2 on TEST_B


SQL> -- using the same columns used to partition IX_TEST_B1.
SQL> -- Note the following error...
SQL>
SQL> CREATE INDEX ix_test_b2 ON test_b(col1, col2)
2 LOCAL;
create index ix_test_b2 on test_b(col1, col2)
*
ERROR at line 1:
ORA-01408: such column list already indexed

SQL> -- Add a primary key constraint, PK_TEST_B, to TEST_B


SQL>
SQL> ALTER TABLE test_b ADD CONSTRAINT pk_test_b

_______________________________________________________________________
Prepared by M.A.Asad Page 107 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
2 PRIMARY KEY (col1, col2);

Table altered.

SQL> -- Attempt to drop index IX_TEST_B1; note the following error...


SQL>
SQL> DROP INDEX ix_test_b1;
drop index ix_test_b1
*
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key

SQL> -- A listing of the indexes and their associated partitions.


SQL>
SQL> SELECT index_name, partition_name, status
2 FROM user_ind_partitions
3 ORDER BY index_name, partition_name;

INDEX_NAME PARTITION_NAME STATUS


----------- --------------- --------
IX_TEST_A IX_TEST_A_1 USABLE
IX_TEST_A IX_TEST_A_2 USABLE
IX_TEST_A IX_TEST_A_3 USABLE
IX_TEST_A IX_TEST_A_4 USABLE

IX_TEST_B1 IX_TEST_B1_1 USABLE


IX_TEST_B1 IX_TEST_B1_2 USABLE

6 rows selected.

SQL> -- Drop the primary key constraint from TEST_A


SQL>
SQL> ALTER TABLE test_a DROP CONSTRAINT pk_test_a;

Table altered.

SQL> -- Drop the primary key constraint from TEST_B


SQL>
SQL> ALTER TABLE test_b DROP CONSTRAINT pk_test_b;

Table altered.

SQL> -- A listing of the indexes and their associated partitions.


SQL> -- Note that while IX_TEST_A, the non-unique local partitioned
SQL> -- index, remains and has a status of USABLE.

_______________________________________________________________________
Prepared by M.A.Asad Page 108 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL> -- IX_TEST_B, the unique global partitioned index, has been
SQL> -- dropped.
SQL>
SQL> SELECT index_name, partition_name, status
2 FROM user_ind_partitions
3 ORDER BY index_name, partition_name;

INDEX_NAME PARTITION_NAME STATUS


--------------- -------------- --------

IX_TEST_A IX_TEST_A_1 USABLE


IX_TEST_A IX_TEST_A_2 USABLE
IX_TEST_A IX_TEST_A_3 USABLE
IX_TEST_A IX_TEST_A_4 USABLE

The primary key uses the underlying index if the index is built using the same columns
as defined in the primary key. This is consistent without regard to whether the index was
created as a unique or non-unique index, or if it is a global or local partioned index. It is
important to note that while in the example a primary key was established on a non-
unique index, this will only occur if the values within the index are in fact unique.
Attempting to enable a primary key constraint when duplicate values are present within
the index will result in the following error:

"ORA-02437: cannot enable (STEELY.PK_TEST_B) - primary key violated."

Two indexes cannot be created using the same ordered columns. This was demonstrated
above when attempting to create a second index on table TEST_B. This resulted in the
following error:

"ORA-01408: such column list already indexed."

However, changing the order of the columns will permit the creation of additional
indexes using the same columns.

Contrary to the previous note, the column order for index IX_TEST_A and the definition
for the primary key PK_TEST_A were reversed. Yet the primary key still used
IX_TEST_A as the underlying index.

When dropping a primary key constraint from a table, the corresponding index is also
dropped if the index was created as a UNIQUE index. This behavior is consistent for
both LOCAL as well as GLOBAL partitioned indexes.

To receive the full benefits of partitioning, users/DBA must use the STORAGE clause
when creating partitioned tables/indices.

How to Use Exchange Partition with IOT Tables


EXCHANGE IOT TABLES

_______________________________________________________________________
Prepared by M.A.Asad Page 109 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
I Introduction

IOT tables appeared with 8.1.5 version. It offers the means to store index and table data
in the same structure.

An example of IOT table will be:

SQL> CREATE TABLE emp_iot


2 (empno NUMBER(4) PRIMARY KEY,
3 ename VARCHAR2(10),
4 sal NUMBER(7,2))
5 ORGANIZATION INDEX INCLUDING ename OVERFLOW
6 PARTITION BY RANGE(empno)
7 (partition emp_p1 VALUES LESS THAN (50) TABLESPACE TBS1,
8 partition emp_p2 VALUES LESS THAN (100) TABLESPACE TBS2,
9 partition emp_p3 VALUES LESS THAN (150) TABLESPACE TBS3,
10 partition emp_p4 VALUES LESS THAN (MAXVALUE) tablespace TBS4);

Table created.

The object will be splitted between two physical segments: a TOP segment and an
optional OVERFLOW
segment which contains a subpart of each row. In 8.1.X version, the RANGE partitioning
was
only supported with the partitioning key which should be a subset of the primary key
defined on the IOT. Since 9.0.1 version, the HASH partitioning is now supported too.

II Usage

For our needs, we will create now a Normal table based on the previous IOT table:

SQL> create table emp_normal


2 (empno NUMBER(4) PRIMARY KEY,
3 ename VARCHAR2(10),
4 sal NUMBER(7,2));

Table created.

You can try now to exchange it with the EMP_IOT table:

SQL> alter table EMP_IOT exchange partition EMP_P1 with table EMP_NORMAL;
alter table EMP_IOT exchange partition EMP_P1 with table EMP_NORMAL
*
ERROR at line 1:
ORA-28653: tables must both be index-organized

It is not supported.

The EXCHANGE PARTITION command will run only between two IOT tables. They
must be defined similarly and equipartitioned between them. Both the source and target

_______________________________________________________________________
Prepared by M.A.Asad Page 110 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
must have overflow segments, or neither can have overflow segments. If you don't
follow this rule, you will get:

ORA-28654: table and partition not overflow compatible

The following table will respect the previous rules:

SQL> CREATE TABLE emp_iot_2


2 (empno NUMBER(4) PRIMARY KEY,
3 ename VARCHAR2(10),
4 sal NUMBER(7,2))
5 ORGANIZATION INDEX INCLUDING ename OVERFLOW;

Table created.

You can now exchange it with the EMP_IOT table:

SQL> alter table EMP_IOT exchange partition EMP_P1 with table EMP_IOT_2;

Table altered.

Since the 9i version, you can define tablespaces with different BLOCKSIZE parameter.
If you define some LOB columns on your IOT and non-partitioned table, you must use
the same DB_nK_CACHE_SIZE parameter for recording any LOB segments.

The example below will illustrate this rule:

If on your database, two tablespaces are available, LOB8K and LOB4K, respectively
using 8K and 4K blocks.

We create the EMP_IOT table with a LOB column type, ADDRESS, stored on
LOB8K tablespace:

SQL> CREATE TABLE EMP_IOT


2 (empno NUMBER(4) PRIMARY KEY,
3 ename VARCHAR2(10),
4 address CLOB,
5 sal NUMBER(7,2))
6 ORGANIZATION INDEX INCLUDING ename OVERFLOW
7 LOB (address) STORE AS (TABLESPACE LOB8K)
8 PARTITION BY RANGE(empno)
9 (partition emp_p1 VALUES LESS THAN (50) TABLESPACE TBS1,
10 partition emp_p2 VALUES LESS THAN (100) TABLESPACE TBS2,
11 partition emp_p3 VALUES LESS THAN (150) TABLESPACE TBS3,
12* partition emp_p4 VALUES LESS THAN (MAXVALUE) tablespace TBS4)
SQL> /

Table created.

_______________________________________________________________________
Prepared by M.A.Asad Page 111 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Then the EMP_IOT_2 non-partitioned with the same LOB column, ADDRESS, stored
on
LOB4K tablespace:

SQL> CREATE TABLE emp_iot_2


2 (empno NUMBER(4) PRIMARY KEY,
3 ename VARCHAR2(10),
4 address CLOB,
5 sal NUMBER(7,2))
6 ORGANIZATION INDEX INCLUDING ename OVERFLOW
7* LOB(address) STORE AS (TABLESPACE LOB4K)
SQL> /

Table created.

The 4 CLOBS segments associated with each table partition are located on the LOB8K
tablespace.

SQL> select table_name, column_name, partition_name, partition_position,


tablespace_name
2 from user_lob_partitions
3 where table_name = 'EMP_IOT'
4 order by partition_position;

TABLE_NAME COLUMN_NAME PARTITION_NAME PARTITION_POSITION


TABLESPACE_NAME
---------- ----------- --------------- ------------------ ----------------
EMP_IOT ADDRESS EMP_P1 1 LOB8K
EMP_IOT ADDRESS EMP_P2 2 LOB8K
EMP_IOT ADDRESS EMP_P3 3 LOB8K
EMP_IOT ADDRESS EMP_P4 4 LOB8K

If you try now to exchange one partition of EMP_IOT with EMP_IOT_2:

SQL> alter table EMP_IOT exchange partition EMP_P1 with table emp_iot_2;
alter table EMP_IOT exchange partition EMP_P1 with table emp_iot_2
*
ERROR at line 1:
ORA-14298: LOB column block size mismatch in ALTER TABLE EXCHANGE
[SUB]PARTITION

The purpose of this document is to highlight the common problems that customers have
faced with regard to partitioned Tables.

This note will help in identifying the problems and how to resolve the problem (if
workarounds are available).

Partition Pruning/Elimination

_______________________________________________________________________
Prepared by M.A.Asad Page 112 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Partition Pruning/Elimination

One of the more desirable features of partitioning is partition pruning also referred to as
partition elimination). This is the process wherein the optimizer transparently eliminates
partitions from the partition access list. This can significantly reduce the amount of data
accessed by a particular query. A common example involves sales data, partitioned
quarterly. Without table partitioning, you may be required to scan the entire table for
dates falling within a particular quarter. With partition pruning, the optimizer will
eliminate 3 of the partitions, and only scan the partition with the relevant range of dates.
The partition key does not have to be a date column. The following example illustrates
parition pruning on a range-partitioned table:

Create the table, 4 partitions:


SQL> create table range_part (col1 number(9))
2 partition by range (col1)
3 (partition p1 values less than (10) tablespace system,
4 partition p2 values less than (20) tablespace system,
5 partition p3 values less than(30) tablespace users,
6 partition p4 values less than (MAXVALUE) tablespace users);

Table created.

Insert 1 row per partition:


SQL> insert into range_part values (1);
SQL> insert into range_part values (11);
SQL> insert into range_part values (21);
SQL> insert into range_part values (31);
SQL> commit;

Commit complete.

Explain a query that will access 1 partition:


SQL> EXPLAIN PLAN
2 set statement_id = 'range_part'
3 FOR
4 SELECT *
5 FROM range_part
6 WHERE col1= 15;

Explained.

Review the explain plan to verify that partition pruning will occur:
(this query is modified from a forum on asktom.oracle.com)
SQL> column operation format a16;
SQL> column options format a16;
SQL> column start format a5;
SQL> column stop format a4;
SQL> SELECT LPAD(' ', 2*(LEVEL-1))||operation operation,
2 options || '(' || object_name || ')' options, position,

_______________________________________________________________________
Prepared by M.A.Asad Page 113 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
3 PARTITION_START "START", PARTITION_STOP "STOP"
4 FROM plan_table
5 START WITH id = 0 AND statement_id = 'range_part'
6 CONNECT BY PRIOR id = parent_id AND statement_id = 'range_part'
7 /

OPERATION OPTIONS POSITION START STOP


---------------- ---------------- ---------- ----- ----
SELECT STATEMENT () 1
TABLE ACCESS FULL(RANGE_PART) 12 2

The start and stop columns refer to the start_partition and stop_partition columns in the
plan_table. We can see here that partition 2 is the only partition that will be accessed.
We have eliminated 3/4 of the partitions.

Verify that the correct partition will be accessed:


SQL> SELECT partition_position,
2 partition_name
3 FROM dba_tab_partitions
4 WHERE table_name='RANGE_PART';

PARTITION_POSITION PARTITION_NAME
------------------ ------------------------------
1 P1
2 P2
3 P3
4 P4

TKPROF (versions 8.1.6 and later) will also verify that partition pruning has taken place
for a particular statement. Consider the following example:

SQL> alter session set sql_trace = true;

Session altered.

SQL> SELECT *
2 FROM range_part
3 WHERE col1= 15;

SQL> !
% tkprof *.trc 1.out explain=sys/change_on_install

Portion of the tkprof output:


Rows Row Source Operation
------- ---------------------------------------------------
0 TABLE ACCESS FULL RANGE_PART PARTITION: START=2 STOP=2

Rows Execution Plan


------- ---------------------------------------------------

_______________________________________________________________________
Prepared by M.A.Asad Page 114 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
0 SELECT STATEMENT GOAL: CHOOSE
0 TABLE ACCESS (FULL) OF 'RANGE_PART' PARTITION: START=2 STOP=2

The start and stop partitions are once again reflected in the explain plan.

One final method to observe the partition pruning is with event 10128. This should not
be necessary under normal circumstances, but provides another method to verify the
execution plan. This will generate a trace file, much like sql_trace, of the following
format:

SQL> alter session set events '10128 trace name context forever, level 2';

Session altered.

SQL> SELECT *
2 FROM range_part
3 WHERE col1 IN (5, 35);

SESSION ID:(9.44091) 2001-11-16 13:59:43.515


Partition Iterator Information:
partition level = PARTITION
call time = RUN
order = ASCENDING
Partition iterator for level 1:
iterator = ARRAY {count= 2, maximum= 2}
index = 0
current partition: part# = 0, subp# = 65535, abs# = 0
current partition: part# = 2, subp# = 65535, abs# = 2

We can see that it has accessed two partitions (count=2), and the partitions (partition 0
equates to partition_position 1-the first element in an array is 0).

Partition pruning can also occur on sub-partitions and partitioned indexes assuming the
relevant key is found in the WHERE clause of the sql statement).

See
"http://asktom.oracle.com/pls/ask/f?p=4950:8:267626::NO::F4950_P8_DISPLAYID,
F4950_P8_CRITERIA:1112305237628,%7Bsubpartition%7D%20and
%20%7Belimination%7D" for an example and discussion of subpartition elimination.

Exchange Partition – Examples


Exchange Partitions

_______________________________________________________________________
Prepared by M.A.Asad Page 115 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
You can convert a partition (or subpartition) into a nonpartitioned table, and a
nonpartitioned table into a partition (or subpartition) of a partitioned table by exchanging
their data segments. You can also convert a hash-partitioned table into a partition of a
composite-partitioned table.
In this case, the exchange is only possible if the hash table and the target composite
partition are equipartitioned.

Exchanging table partitions is most useful when you have an application using
nonpartitioned tables which you want to convert to partitions of a partitionedtable.
For example, you may already have partition views that you want to migrate into
partitioned tables.
A scenario for converting a partitioned view is presented in "Converting a Partition View
into a Partitioned Table".

Exchanging partitions also facilitates high-speed data loading when used with
transportable tablespaces.

When you exchange partitions, logging attributes are preserved, but you can optionally
specify if local indexes are also to be exchanged, and if rows must be validate for proper
mapping.

To exchange a partition of a range or hash-partitioned table with a non partitioned table,


you use the ALTER TABLE...EXCHANGE PARTITION statement.

The example below presents an example which is done between a non partitioned table
T2, and a range partitioned table T1.

1- First, create a range partitioned table & Populate it:

create table t1
( id number(2),
name varchar2(15))
partition by range (id)
(partition p1 values less than (10),
partition p2 values less than (20),
partition p3 values less than (30))
/

insert into t1 values (1, '1');


insert into t1 values (11, '11');
insert into t1 values (21, '21');

SQL> select * from t1;

ID NAME
---------- ---------------
11
11 11
21 21

_______________________________________________________________________
Prepared by M.A.Asad Page 116 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
2- Create a non partitioned table t2 with same structure of t1:
SQL> create table t2 (id number(2), name varchar2(15));

Table created.

3- Exchange partition p1 of table t1 with table t2;


SQL> alter table t1 exchange partition p1 with table t2;

Table altered.

SQL> select * from t2;

ID NAME
---------- ---------------
11

SQL> select * from t1;

ID NAME
---------- ---------------
11 11
21 21

4- Exchange table t3 with partition p1 of table t1:

SQL> create table t3 (id number(2), name varchar2(15));

Table created.

SQL> insert into t3 values (2,'22');

1 row created.

SQL> commit;

Commit complete.

SQL> alter table t1 exchange partition p1 with table t3;

Table altered.

SQL> select * from t1;

ID NAME
---------- ---------------
2 22
11 11
21 21

SQL> select * from t3;

_______________________________________________________________________
Prepared by M.A.Asad Page 117 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

no rows selected

Partition Pruning and Joins


This article explains some of the problems people may be having with understanding
partition pruning/elimination.

This article covers partition pruning that relies on data from another table to provide
partition key information. It does not cover pruning that is enabled by predicates against
the partition key on the partition table.

 If elimination does not occur automatically, then can elimination be forced with
hints? If so then this means that the optimizer is choosing a less costly plan that
just happens not to require the elimination of partitions.

 Is the partition key value in place when the partition is accessed? In other words
is the join order correct? If the partition key value is dependant on a join to
another table, then that table MUST be accessed PRIOR to the partitioned table in
order for partition elimination to be able to take place.

 Is elimination possible with the join type that is being used? Only nested loop
joins (and certain types of hash joins) allow predicates from the outer table to
seed a lookup on the inner table. If the query in question uses a normal hash join
or a sort merge join then join predicates cannot be used for partition elimination.

 If bind variables are used in the query then at parse time it may only be possible
to tell that elimination can occur as opposed to which partitions can be
eliminated. This DOES NOT mean that elimination cannot occur with bind
variables.

A number of examples are presented to illustrate these scenarios.


Partition Pruning and Joins

At parse time the cost based optimizer determines (among others) 2 key things about
partitions:

Can partitions be eliminated at runtime?


If so, exactly which partitions these are.

Often it may only be able to determine that elimination is possible. When dealing with
partition tables, there is often confusion as to whether partition elimination has occurred
or not. This is because it is not particularly easy, to the uninitiated, to tell if elimination
has actually occurred or not.

The key to determining if rows can and have been eliminated is as follows:

PLAN TABLE

_______________________________________________________________________
Prepared by M.A.Asad Page 118 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Remember that the plan table CAN ONLY tell you what is PREDICTED to happen at
run time. Partitions that will be accessed are recorded in the PARTITION_START and
PARTITION_STOP columns of the plan table. These columns have 4 potential values:

a number - the partition number for the start or stop partition


KEY/ROW LOCATION - partition elimination is possible but the partition number
will be identified at run time
INVALID - partition elimination is possible but the range of partitions
is invalid

This is probably the most long winded and complex way of finding if partitions have
been accessed or not. It involves recording exactly which files and blocks are spanned by
each individual partitions and then looking at the actual blocks read to determine is any
of these are outside the expected partitions. This can be time consuming but can be
speeded up by sensible use of sorting and unix tools such as grep and awk (or
equivalents) to strip out exactly the data that is required. This should be regarded as a last
resort.

Grep example to gather all the normal file io waits:

grep 'WAIT' TRACE.TRC | grep 'db file s' > out.txt

awk and sort example to pick out the file and block numbers and perform a numeric sort:

awk '{print $9" "$10}' out.txt |sort -n > final_out.txt

Common Scenarios:

The Examples below use 2 tables: dept and range_part, range_part is created as follows:

create table range_part (col1 number(9))


partition by range (col1)
(partition p1 values less than (10) tablespace system,
partition p2 values less than (20) tablespace system,
partition p3 values less than(30) tablespace users,
partition p4 values less than (MAXVALUE) tablespace users);

insert into range_part values (1);


insert into range_part values (11);
insert into range_part values (21);
insert into range_part values (31);
commit;

1. Eliminated partitions can be determined at parse time


========================================================
In this case the optimizer can tell, at parse time, from information in the query combined
with dictionary information, exactly which partitions need to be accessed.

_______________________________________________________________________
Prepared by M.A.Asad Page 119 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
The PARTITION_START and PARTITION_START columns in the plan table will
contain the partition numbers for the start and stop partitions in the range of partitions to
be accessed.

Example: The following example selects where col1 is equal to 15. this should only
access partition 2.

explain plan for select * from range_part where col1=15;


start $ORACLE_HOME/rdbms/admin/utlxpls.sql

Plan Table
--------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
--------------------------------------------------------------------------------
| SELECT STATEMENT | | 1 | 13 | 1| | |
| TABLE ACCESS FULL |RANGE_PAR | 1 | 13 | 1| 2| 2|
--------------------------------------------------------------------------------

The Pstart and Pstop columns clearly show that the optimizer believes that only partition
2 is required to satisfy this query.

2. Eliminated partitions can only be determined at run time


===========================================================

In this example the actual partition numbers cannot be determined, only that there is a
possibility that partitions can be eliminated at runtime. The PARTITION_START and
PARTITION_START columns in the plan table will contain either KEY or ROW
LOCATION depending on the nature of the query and the partition itself.

explain plan for


select /*+ ordered use_nl(p) */ col1
from dept d, range_part p
where d.deptno = p.col1
and d.loc = 'DALLAS';
start /oracle2/app/oracle/product/8.1.7/rdbms/admin/utlxpls.sql

Plan Table
--------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
--------------------------------------------------------------------------------
| SELECT STATEMENT | | 3 | 102 | 2| | |
| NESTED LOOPS | | 3 | 102 | 2| | |
| TABLE ACCESS FULL |DEPT | 1 | 21 | 1| | |
| PARTITION RANGE ITERATOR| | | | | KEY | KEY |
| TABLE ACCESS FULL |RANGE_PAR | 328 | 4K| 1 | KEY | KEY |
--------------------------------------------------------------------------------

_______________________________________________________________________
Prepared by M.A.Asad Page 120 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Note that the operation is PARTITION RANGE ITERATOR and the Pstart and Pstop
columns show KEY. This indicates that the elimination of partitions will be determined
at runtime dependant on the values returned from the dept table.

3. Elimination is not occurring due to the join order


=====================================================
If the partition is accessed prior to the tables that must be read to provide data values
required to allow elimination to take place then elimination will not occur.

explain plan for


select /*+ ordered use_nl(d) */ col1
from range_part p, dept d
where d.deptno = p.col1
and d.loc = 'DALLAS';

start /oracle2/app/oracle/product/8.1.7/rdbms/admin/utlxpls.sql

Plan Table
--------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
--------------------------------------------------------------------------------
| SELECT STATEMENT | | 3 | 102 | 329 | | |
| NESTED LOOPS | | 3 | 102 | 329 | | |
| PARTITION RANGE ALL | | | | | 1| 4|
| TABLE ACCESS FULL |RANGE_PAR | 328 | 4K| 1| 1| 4|
| TABLE ACCESS FULL |DEPT | 1 | 21 | 1| | |
--------------------------------------------------------------------------------

Note that the operation is PARTITION RANGE ALL and the Pstart and Pstop columns
show the first and last partitions in the table. This indicates that all partitions will be
accessed. Runtime elimination CANNOT occur.

4. Elimination is not occurring due to the join method


======================================================
Before 8.1.6

For elimination to occur on a join based predicate, the join method must allow values
from the non-partitioned side of the join to be passed to the partition side. Only Nested
loops and certain types of hash joins where a subquery is dynamically built can allow
elimination to occur.

In this example a Hash Join is forced using a hint. Because Hash Joins do not normally
allow join key values to drive lookups on the inner table, all partitions must be accessed.

explain plan for


select /*+ ordered use_hash(p) */ col1
from dept d, range_part p
where d.deptno = p.col1
and d.loc = 'DALLAS';
start /oracle2/app/oracle/product/8.1.7/rdbms/admin/utlxpls.sql

_______________________________________________________________________
Prepared by M.A.Asad Page 121 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Plan Table
--------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
--------------------------------------------------------------------------------
| SELECT STATEMENT | | 3 | 102 | 3| | |
| HASH JOIN | | 3 | 102 | 3| | |
| TABLE ACCESS FULL |DEPT | 1 | 21 | 1| | |
| PARTITION RANGE ALL | | | | | 1| 4|
| TABLE ACCESS FULL |RANGE_PAR | 328 | 4K| 1| 1| 4|
--------------------------------------------------------------------------------

Note that the operation is PARTITION RANGE ALL and the Pstart and Pstop columns
show the first and last partitions in the table. This indicates that all partitions will be
accessed. In this case runtime elimination CANNOT occur.

8.1.6 and above

In 8.1.6 the code was enhanced to allow elimination to occur with a hash join. It was
recognized that the non-partitioned table contains the information to eliminate partitions.
If this information is compared with the dictionary information regarding partition
ranges, then a list of partitions to be accessed can be generated. This activity only occurs
if certain cost and selectivity criteria are met. The elimination IS NOT done in the join
step itself, rather it is performed as recursive SQL independently of the join itself and
therefore elimination is not join order dependant. In this case the following plan can be
seen:

Note that this plan was forced using 2 parameters to override the cost and selectivity
criteria mentioned above. The relationship between these parameters and the costing
algorithms is complex and is beyond the scope of this article:

alter session set "_subquery_pruning_cost_factor"=1; -- overrides cost restrictions


alter session set "_subquery_pruning_reduction"=100; -- overrides selectivity restrictions

explain plan for


select /*+ ordered use_hash(p) */ col1
from dept d, range_part p
where d.deptno = p.col1
and d.loc = 'DALLAS';
start /oracle2/app/oracle/product/8.1.7/rdbms/admin/utlxpls.sql

Plan Table
--------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
--------------------------------------------------------------------------------
| SELECT STATEMENT | | 3 | 102 | 3| | |
| HASH JOIN | | 3 | 102 | 3| | |
| TABLE ACCESS FULL |DEPT | 1 | 21 | 1| | |
| PARTITION RANGE ITERATOR| | | | | KEY | KEY |
| TABLE ACCESS FULL |RANGE_PAR | 328 | 4K| 1 | KEY | KEY |

_______________________________________________________________________
Prepared by M.A.Asad Page 122 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
--------------------------------------------------------------------------------

Note that the operation is PARTITION RANGE ITERATOR and the Pstart and Pstop
columns show KEY. This indicates the elimination of partitions will be determined at
runtime dependant on the values returned from a recursive lookup of the dept table
during the PARTITION RANGE ITERATOR step.

5. Elimination is potentially possible with joins and bind variables


===============================================================
The fact that there is a bind variable on the query DOES NOT prevent the optimizer from
spotting the potential for elimination at runtime.

variable b1 varchar2(10);
explain plan for
select /*+ ordered use_nl(p) */ col1
from dept d, range_part p
where d.deptno = p.col1
and d.loc =:b1;
start /oracle2/app/oracle/product/8.1.7/rdbms/admin/utlxpls.sql

Plan Table
--------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
--------------------------------------------------------------------------------
| SELECT STATEMENT | | 3 | 102 | 2| | |
| NESTED LOOPS | | 3 | 102 | 2| | |
| TABLE ACCESS FULL |DEPT | 1 | 21 | 1| | |
| PARTITION RANGE ITERATOR| | | | | KEY | KEY |
| TABLE ACCESS FULL |RANGE_PAR | 328 | 4K| 1 | KEY | KEY |
--------------------------------------------------------------------------------

Note that the operation is PARTITION RANGE ITERATOR and the Pstart and Pstop
columns show KEY. This indicates the elimination of partitions will be determined at
runtime dependant on the values returned from the dept table.

How to Split Partition of Heavily Used Table:


=============================================

Scenario:
 Large production database.
 Database contains table called BIG, that is partitioned by sequence number.
 BIG has an oversized partition called Colorado that has grown to a size of 30+
million rows.
 It is determined that this oversized partition should be split into 50 smaller
partitions.
 A job exists that constantly inserts into the table called BIG

_______________________________________________________________________
Prepared by M.A.Asad Page 123 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
If the partition is split using the syntax ALTER TABLE BIG SPLIT PARTITION...., you
receive an error is returned; ORA-54 "resource busy and acquire with NOWAIT
specified".

To split the partition the insert job will have to be stopped. Depending on the size of the
table, it may take up to a day to split or add a new partition.

The downtime associated with the split is too great as no DML can be performed against
the table during that time.

Are there any other options?


Suggestions:

1. Modify the INSERT job in order to eliminate the partition in question. This should
allow separate actions against this partition.

2. If the first option is not possible, try the following steps that give a less time
consuming scenario than using the SPLIT PARTITION syntax.

A. Create table NEW ....- nonpartitioned, with the storage large enough to store all of
the data from the oversized partition in table BAD.

B. Alter table BIG exchange partition Colorado with table NEW without validation;

-->the BAD partition Colorado which was previously a segment now becomes a table
and table NEW becomes a partition.

No data is actually moved, only the data dictionary gets updated.

C. Alter table BIG truncate partition Colorado;

-->this should be faster than delete. Disable referential integrity constraints if needed,
before truncating.

D. Now the 50 smaller tables from table NEW can be created - each table will then be
exchanged for a partition - NEW1....NEW50

E. Alter table BIG split partiton BAD.... - as many times as needed


(50? ---> GOOD1,..., GOOD50)

--> There is no data to move (BAD was truncated), so it should be quick.

F. Insert into BIG partition (GOOD1) select * from NEW1;

--> for each of new partitions;

Explanation:

_______________________________________________________________________
Prepared by M.A.Asad Page 124 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
The most time consuming step will be #E above, where the process is moving the actual
data into smaller tables that will be exchanged with partitions later.

The benefits of using the alternative steps outlined in suggestion 2 are one, the process
requires less time and two, and the BIG table is still accessible to users and processes
during the time that the data is being divided into smaller sections.

How to Analyze a Table at the Partition Level


How to analyze table at the partition level

Oracle8/8i/9i allows the ability to gather statistics at the partition level for a table and
index. Gathering statistics at the partition level improves performance because you can
issue multiple analyze table commands in parallel, each against different partition.

The command to analyze a particular partition is:

1) "analyze table <tablename> partition (<partition_name>) compute statistics;"

It computes exactly the statistics on the partition table.


OR
2) "analyze table <tablename> partition (<partition_name>) estimate statistics sample
<rows, percent>"

It computes the statistics only on a certain part of data, which is set as number of row
tables, by default 1064, or on a percentage. I you specify more than half of the data, the
statistics will be computed exactly by Oracle.

Note:
Starting Oracle 8i, Oracle recommends using DBMS_STATS to compute statistics on
Partitioned objects.

Please refer to the following note for more details

Moving Data from Non-Partitioned Tables to Partitioned Tables: Oracle 8.x

Transferring Rows from a Non-Partitioned Table to a Partitioned Table:


===============================================================
In the case in which the table of departure contains a high number of records, the
movement of the data may burden the database in terms of I/O activity, usage of the
rollback segment, the redolog, and the buffer_cache.

Three possible routes exist for the migration of rows:

1. The simplest syntactical route is the following:

INSERT INTO <partitioned_table> SELECT * FROM <Non-partition_table>;

You may create the partitioned table with NOLOGGING option to reduce the amount of
redologs generated during this operation and change it back to LOGGING after you
finish. Also to speed up this process you may use Parallel execution.

_______________________________________________________________________
Prepared by M.A.Asad Page 125 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

2. SQL*Loader can also be used, taking advantage of


UNRECOVERABLE (NOLOGGING), DIRECT, PARALLEL options

3. Partition Exchange method:

ALTER TABLE <table_name> EXCHANGE PARTITION <partition_name>


WITH <table_name>;

ALTER TABLE EXCHANGE PARTITION can be used to convert a partition (or


subpartition) into a non-partitioned table and a non-partitioned table into a partition (or
subpartition) of a partitioned table by exchanging their data and index segments.

===============================================================
Example:

CREATE TABLE "LEGALDB"."TAB1" ("ID" NUMBER, "DATA" DATE, "TEXT"


VARCHAR2(100))
STORAGE ( INITIAL 10M NEXT 10M);

SQL> select count(*) from tab1;

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

CREATE TABLE partizionata(ID NUMBER, periodo DATE, TEXT VARCHAR2(100))


PARTITION BY RANGE (periodo)
(PARTITION Papril
VALUES LESS THAN (to_date('30-04-2000','dd-mm-yyyy'))
STORAGE ( INITIAL 10M NEXT 10M),
PARTITION Pmay
VALUES LESS THAN (to_date('31-05-2000','dd-mm-yyyy'))
STORAGE ( INITIAL 10M NEXT 10M),
PARTITION Pjune
VALUES LESS THAN (to_date('30-06-2000','dd-mm-yyyy'))
STORAGE ( INITIAL 10M NEXT 10M));

SQL>
1 select data,
2 DBMS_ROWID.ROWID_OBJECT(a.rowid) "OBJ#",
3 DBMS_ROWID.ROWID_BLOCK_NUMBER(a.rowid) "BLOCK#",
4 DBMS_ROWID.ROWID_RELATIVE_FNO(a.rowid) "FILE#",
5 substr(o.OBJECT_NAME ,1,10) "ObjNAME",
6 substr(o.SUBOBJECT_NAME ,1,10) "SUBNAME",
7 substr(o.OBJECT_TYPE ,1,10) "ObjTyp"
8 from
9 partizionata a, user_objects o

_______________________________________________________________________
Prepared by M.A.Asad Page 126 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
11 where DBMS_ROWID.ROWID_OBJECT(a.rowid) = o.object_id
SQL> /

DATA OBJ# BLOCK# FILE# ObjNAME SUBNAME ObjTyp


--------- ---------- ---------- ---------- ---------- ---------- ----------
15-APR-00 13947 5123 7 PARTIZIONA Papril TABLE PART
15-JUN-00 13949 15363 7 PARTIZIONA Pjune TABLE PART
15-MAY-00 13948 10243 7 PARTIZIONA Pmay TABLE PART

===============================================================
Possibility 1. INSERT INTO SELECT

Consider the state of the RedoLog and the Rollback Segment


before the execution of the insert

SQLWKS> select * from v$log;


GROUP# THREAD# SEQUENCE#
---------- ---------- ----------
3 1 1318
4 1 1317

SQLWKS> select * from v$rollstat;


USN EXTENTS RSSIZE
---------- ---------- ----------
0 12 612352
2 8 1062912
3 8 1052672
4 8 1048576
5 8 1062912

--->run Insert<----

SQLWKS> insert into partizionata select * from tab1;


262144 rows processed.

Parse 0.01 (Elapsed) 0.00 (CPU)


Execute/Fetch 69.79 (Elapsed) 0.00 (CPU)
Total 69.80 0.00

Estimate after the execution of statement


the state of RedoLog and the Rollback Segment

_______________________________________________________________________
Prepared by M.A.Asad Page 127 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

SQLWKS> select * from v$log;


GROUP# THREAD# SEQUENCE#
---------- ---------- ----------
3 1 1320
4 1 1321

SQLWKS> select * from v$rollstat;


USN EXTENTS RSSIZE
---------- ---------- ----------
0 12 612352
2 10 1329152
3 8 1052672
4 8 1048576
5 8 1062912

===============================================================
Possibility 2. SQL*LOADER

Consider the state of the RedoLog and the Rollback Segment


before the execution of the loading

SQLWKS> select * from v$log;


GROUP# THREAD# SEQUENCE#
---------- ---------- ----------
3 1 1320
4 1 1321

SQLWKS> select * from v$rollstat;


USN EXTENTS RSSIZE
---------- ---------- ----------
0 12 612352
2 8 1062912
3 8 1052672
4 8 1048576
5 8 1062912

--->run loading<----

We obtain the files of data give to load with SQL*Loader

april.sql:

set heading off

_______________________________________________________________________
Prepared by M.A.Asad Page 128 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
set pagesize 0
set linesize 1000
select id||':'||to_char(data,'dd-mon-yyyy')||':'||text
from tab1 where data < to_date('30-04-2000','dd-mm-yyyy') ;
quit;
/

>>sqlplus -s legaldb/legaldb @april.sql > april.dat

may.sql:

set heading off


set pagesize 0
set linesize 1000
select id||':'||to_char(data,'dd-mon-yyyy')||':'||text
from tab1 where data < to_date('31-05-2000','dd-mm-yyyy') and
data >= to_date('30-04-2000','dd-mm-yyyy')
/
quit;

>>sqlplus -s legaldb/legaldb @may.sql > may.dat

june.sql
set heading off
set pagesize 0
set linesize 1000
select id||':'||to_char(data,'dd-mon-yyyy')||':'||text
from tab1 where data < to_date('30-06-2000','dd-mm-yyyy') and
data >= to_date('31-05-2000','dd-mm-yyyy')
/
quit;

>>sqlplus -s legaldb/legaldb @june.sql > june.dat

Files ctl by SQL*Loader:

april.ctl:

unrecoverable load data


infile 'april.dat'
append
into table partizionata
partition (papril)
fields terminated by ':' (id, periodo,text)

may.ctl:

unrecoverable load data

_______________________________________________________________________
Prepared by M.A.Asad Page 129 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
infile 'may.dat'
append
into table partizionata
partition (pmay)
fields terminated by ':' (id, periodo,text)

june.ctl:

unrecoverable load data


infile 'june.dat'
append
into table partizionata
partition (pjune)
fields terminated by ':' (id, periodo,text)

Start SQL*Loader:

time sqlldr control=april.ctl parfile=par.par &


time sqlldr control=may.ctl parfile=par.par &
time sqlldr control=june.ctl parfile=par.par &

Load completed - logical record count 65539.

real 0m51.488s
user 0m0.050s
sys 0m0.180s

Load completed - logical record count 65539.

real 1m5.368s
user 0m0.150s
sys 0m0.130s

Load completed - logical record count 131075.

real 1m18.762s
user 0m0.140s
sys 0m0.340s
----------------
Estimate after the execution of loading
the state of RedoLog and the Rollback Segment

SQLWKS> select * from v$log;


GROUP# THREAD# SEQUENCE#
---------- ---------- ----------
3 1 1320

_______________________________________________________________________
Prepared by M.A.Asad Page 130 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
4 1 1321

SQLWKS> select * from v$rollstat;


USN EXTENTS RSSIZE
---------- ---------- ----------
0 12 612352
2 8 1062912
3 8 1052672
4 8 1048576
5 8 1062912

===============================================================
Possibility 3.ALTER TABLE EXCHANGE

Example:

SQL> CREATE TABLE emp1


2 (empno1 number(4),sal NUMBER(7,2))
3 PARTITION BY RANGE(sal)
4 (partition emp1_p1 VALUES LESS THAN (2000),
5 partition emp1_p2 VALUES LESS THAN (4000));
Table created.

SQL> SELECT * FROM emp;


EMPNO ENAME JOB MGR HIREDATE SAL
--------- ---------- --------- --------- --------- ---------
7369 SMITH CLERK 7902 17-DEC-80 800
7499 ALLEN SALESMAN 7698 20-FEB-81 1600
7521 WARD SALESMAN 7698 22-FEB-81 1250
7566 JONES MANAGER 7839 02-APR-81 2975
7654 MARTIN SALESMAN 7698 28-SEP-81 1250
7698 BLAKE MANAGER 7839 01-MAY-81 2850
7782 CLARK MANAGER 7839 09-JUN-81 2450
7788 SCOTT ANALYST 7566 19-APR-87 3000
7839 KING PRESIDENT 17-NOV-81 5000
7844 TURNER SALESMAN 7698 08-SEP-81 1500
7876 ADAMS CLERK 7788 23-MAY-87 1100
7900 JAMES CLERK 7698 03-DEC-81 950
7902 FORD ANALYST 7566 03-DEC-81 3000
7934 MILLER CLERK 7782 23-JAN-82 1300
14 rows selected.

SQL> CREATE TABLE essai1 as SELECT sal


FROM emp WHERE sal<2000;
Table created.

SQL> CREATE TABLE essai2 as SELECT sal FROM emp WHERE sal
BETWEEN 2000 AND 3999;
Table created.

_______________________________________________________________________
Prepared by M.A.Asad Page 131 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

SQL> alter table emp1 exchange partition emp1_p1


with table essai1;
Table altered.

SQL> alter table emp1 exchange partition emp1_p2


with table essai2;
Table altered.

Partitioned Indexes: Global, Local, Prefixed and Non-Prefixed


To differentiate between types of partitioned indexes.

The different types of partitioned indexes are among the least-understood features
associated with Oracle8x partitioned tables. This note aims to set out the definitions of
each index type.

A partitioned table may have either partitioned or non-partitioned indexes; a non-


partitioned table may have partitioned indexes. In practice, though, most partitioned
indexes will be on partitioned tables.

LOCAL INDEXES

A local index is equi-partitioned with the underlying table, so each index partition has
entries for rows in a single table partition. The partition bounds will be the same as for
the table itself.

A local index may be prefixed or non-prefixed. A prefixed index is partitioned on the


leftmost column(s) in the index. Since a local index, by definition, is partitioned on the
same key as the table, a local prefixed index will have the table partition key as its
leftmost column.

So if we use as an example the following partitioned table:

CREATE TABLE dept


(deptno NUMBER NOT NULL,
dname VARCHAR2(10) NOT NULL,
loc VARCHAR2(14))
PARTITION BY RANGE (deptno)
(PARTITION part1 VALUES LESS THAN (30),
PARTITION part2 VALUES LESS THAN (MAXVALUE));

Then a local prefixed index would be created as follows:

CREATE INDEX deptloc1_idx ON dept(deptno) LOCAL;

though we could be much more specific about partition names and tablespaces if we
chose.

_______________________________________________________________________
Prepared by M.A.Asad Page 132 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Local non-prefixed indexes will not have the table's partitioning key as their leftmost
column. For example:

CREATE INDEX deptloc2_idx ON dept(loc) LOCAL;

Each partition of a non-prefixed local index will of course contain the full
range of possible key values, as shown in the diagram below:

| |
------- --------
| | | |
A.. Z.. A.. Z.. (for a VARCHAR2 column)

This may look inefficient, but remember that we can search all the index partitions in
parallel.

GLOBAL INDEXES
A global index is partitioned, but along different lines from the table. It may be on the
same key, but different ranges; or it could be on a different key altogether.

Global non-prefixed indexes are not supported. This means that the index partitioning
key must always be the leftmost index column. Anything else will raise the error:

ORA-14038: GLOBAL partitioned index must be prefixed

Most examples of global indexes, in documentation and training material, use the same
partitioning key as for the table, with different ranges. But global indexes are most
powerful if they are partitioned on a different column from the table. For example
(recalling that the DEPT table itself is partitioned on DEPTNO):

CREATE INDEX dept_idx ON dept(dname)


GLOBAL PARTITION BY RANGE (dname)
(PARTITION p1 VALUES LESS THAN ('N'),
PARTITION p2 VALUES LESS THAN (MAXVALUE));

To illustrate the usefulness of global indexes, imagine that we have a large fact table
partitioned on a DATE column. We frequently need to search the table on a VARCHAR2
column (VCOL) which is not part of the table's partition key. Assume that there are
currently 12 partitions in the table.

We could use 2 possible methods:

A local non-prefixed index on VCOL:

| |
------- -------
| | (10 more | |
es: A.. Z.. partitions here) A.. Z..

Or a global prefixed index on VCOL:

_______________________________________________________________________
Prepared by M.A.Asad Page 133 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

| |
-------------- ----------------------
| | (10 more | |
Values: A.. D.. partitions here) T.. Z..

A global prefixed index would usually be the best choice for a unique index on our
example VCOL column. For nonunique indexes, the issue is whether we can use parallel
index searches (local non-prefixed) or whether we need a serial search, even at the
expense of the greater maintenance problems of global indexes.

ADDING A NEW PARTITION TO AN IOT IT IS PLACED IN THE DEFAULT


TABLESPACE

ADDING A NEW PARTITION TO AN IOT IT IS PLACED IN THE DEFAULT


TABLESPACE

When adding a partition to an index organized partitioned table the partition is placed in
the DEFAULT TABLESPACE instead of the specified tablespace.

Workaround: alter table and move partition to the other tablespace. It is not reproducible
on Oracle 9.0.1

TESTCASE:
$ sqlplus jbarba/jbarba

CREATE TABLE sales(acct_no NUMBER(5), acct_name CHAR(30), amount_of_sale


NUMBER(6),
week_no INTEGER, sale_details VARCHAR2(1000),
PRIMARY KEY (acct_no, acct_name, week_no))
ORGANIZATION INDEX INCLUDING week_no
OVERFLOW TABLESPACE TOOLS
PARTITION BY RANGE (week_no)
(PARTITION VALUES LESS THAN (5) TABLESPACE JBARBA01,
PARTITION VALUES LESS THAN (9) TABLESPACE JBARBA01);

-- Default tablespace for user JBARBA is USERS


select default_tablespace from user_users
where username='JBARBA';

DEFAULT_TABLESPACE
------------------------------
USERS
col tablespace_name format a18
col segment_name format a24
col partition_name format a24
col segment_type format a15

SELECT tablespace_name, segment_name, partition_name

_______________________________________________________________________
Prepared by M.A.Asad Page 134 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
FROM user_segments;

TABLESPACE_NAME SEGMENT_NAME PARTITION_NAME


------------------ ------------------------ ------------------------
TOOLS SYS_IOT_OVER_92543 SYS_P14
TOOLS SYS_IOT_OVER_92543 SYS_P13
JBARBA01 SYS_IOT_TOP_92543 SYS_P14
JBARBA01 SYS_IOT_TOP_92543 SYS_P13

Alter table sales add partition VALUES LESS THAN (13) TABLESPACE JBARBA01;

SELECT tablespace_name, segment_name, partition_name


FROM user_segments;

TABLESPACE_NAME SEGMENT_NAME PARTITION_NAME


------------------ ------------------------ ------------------------
TOOLS SYS_IOT_OVER_92543 SYS_P15
TOOLS SYS_IOT_OVER_92543 SYS_P14
TOOLS SYS_IOT_OVER_92543 SYS_P13
JBARBA01 SYS_IOT_TOP_92543 SYS_P14
USERS SYS_IOT_TOP_92543 SYS_P15
JBARBA01 SYS_IOT_TOP_92543 SYS_P13

Note: Partitions created in TOOLS tablespace are the Overflow partitions


as specified in the create table statement. The new added partition
(SYS_P15) was created in USERS tablespace even though we specified
tablespace JBARBA01

WORKAROUND:

alter table sales move partition SYS_P15 tablespace JBARBA01;

SELECT tablespace_name, segment_name, partition_name


FROM user_segments;

TABLESPACE_NAME SEGMENT_NAME PARTITION_NAME


------------------ ------------------------ ------------------------
TOOLS SYS_IOT_OVER_92543 SYS_P15
TOOLS SYS_IOT_OVER_92543 SYS_P14
TOOLS SYS_IOT_OVER_92543 SYS_P13
JBARBA01 SYS_IOT_TOP_92543 SYS_P14
JBARBA01 SYS_IOT_TOP_92543 SYS_P15
JBARBA01 SYS_IOT_TOP_92543 SYS_P13

How To Establish Default Storage For Partitioned Table


SETTINGS OF DEFAULT STORAGE CLAUSES

The storage parameters (initial, next, minextents, maxextents, pctincrease, freelists,


freelist groups) for partitions, first default to the corresponding default parameters for the
table. If default storage parameters do not exist for the table (values are 'DEFAULT'), the

_______________________________________________________________________
Prepared by M.A.Asad Page 135 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
parameters default to the storage parameters of the tablespace hosting the partition.
Default storage parameters for a partitioned table can be found in
<DBA,USER,ALL>_PART_TABLES columns DEF_INITIAL_EXTENT,
DEF_NEXT_EXTENT, DEF_MIN_EXTENTS, DEF_MAX_EXTENTS,
DEF_PCT_INCREASE, DEF_FREELISTS, DEF_FREELIST_GROUPS.

Default storage parameters are set by the CREATE TABLE statement for all parameters
referenced, and by the MODIFY DEFAULT ATTRIBUTES clause of the ALTER
TABLE statement. All storage parameters that have not been explicitly defined in the
CREATE TABLE statement, will have default values of 'DEFAULT' after table creation.
The default storage parameters for a partitioned table only impact new partitions
(partitions added after the default value was established).

Example:

The following is a simple example demonstrating a table default for the storage
parameter NEXT while all other parameters default to the tablespace storage parameters.
The ALTER TABLE statement MODIFY DEFAULT ATTRIBUTES clause is then used
to establish a default storage value for PCT_INCREASE. Note, the default storage
parameters only impact partitions added after the parameter was set.

All the following tests are done on a database which uses 2k block size.

SQL> /* Create a partitioned table. */


SQL> --
SQL> -- Note: Storage parameters not defined at the table level
SQL> -- default to the storage parameters defined for the tablespace
SQL> --
SQL> create table table_w_part
2 (id number not null,
3 name char(1) not null)
4 storage (next 4k)
5 partition by range (id)
6 (partition id1 values less than (100)
7 tablespace users
8 storage (pctincrease 20))
9 /

Table created.

SQL> /* Add a partition. */


SQL> alter table table_w_part
2 add partition id2 values less than (200)
3 /

Table altered.

SQL> -- Note: The pct_increase for the new partition has the

_______________________________________________________________________
Prepared by M.A.Asad Page 136 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL> -- storage parameters for the tablespace
SQL> -- while next_extent has the value for the table.
SQL> --
SQL> select partition_name, next_extent, pct_increase
2 from user_tab_partitions
3 /

PARTITION_NAME NEXT_EXTENT PCT_INCREASE


-------------- ----------- -------------
ID2 4096 50
ID1 4096 20
SQL> select next_extent, pct_increase
2 from sys.dba_tablespaces
3 where tablespace_name = 'USERS'
4 /

NEXT_EXTENT PCT_INCREASE
----------- ------------
10240 50

SQL> -- Note: The default storage parameters for the table.


SQL> -- in this example the DEF_NEXT_EXTENT = 2 represents
SQL> -- an extent of 2 blocks, where each block is 2k.
SQL> --
SQL> select table_name, def_next_extent, def_pct_increase
2 from user_part_tables
3 /

TABLE_NAME DEF_NEXT_EXTENT DEF_PCT_INCREASE


------------ --------------- ----------------
TABLE_W_PART 2 DEFAULT

SQL> /* Alter the storage parameter for the table and specify new defaults. */
SQL> alter table table_w_part
2 modify default attributes
3 storage (pctincrease 30)
4 /

Table altered.

SQL> /* Add a new partition */


SQL> alter table table_w_part
2 add partition id3 values less than (300)
3 /

Table altered.

SQL> -- Note: the new partition now has the expected (and desired) results.
SQL> -- and the user_part_tables.def_pct_increase is now set.
SQL> --

_______________________________________________________________________
Prepared by M.A.Asad Page 137 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL> select partition_name, next_extent, pct_increase
2 from user_tab_partitions
3 /

PARTITION_NAME NEXT_EXTENT PCT_INCREASE


-------------- ----------- ------------
ID2 4096 50
ID3 4096 30
ID1 4096 20

SQL> select table_name, def_next_extent, def_pct_increase


2 from user_part_tables
3 /

TABLE_NAME DEF_NEXT_EXTENT DEF_PCT_INCREASE


------------ --------------- ----------------
TABLE_W_PART 2 30

SQL> -- WARNING: Changing the storage parameter


SQL> -- without specifying the MODIFY DEFAULT ATTRIBUTES
SQL> -- changes the storage parameter for all existing partitions
SQL> -- but not the default value.
SQL> --
SQL> alter table table_w_part
2 storage (pctincrease 35)
3 /

Table altered.

SQL> select partition_name, next_extent, pct_increase


2 from user_tab_partitions
3 /

PARTITION_NAME NEXT_EXTENT PCT_INCREASE


-------------- ----------- ------------
ID2 4096 35
ID3 4096 35
ID1 4096 35

SQL> select table_name, def_pct_increase


2 from user_part_tables
3 /

TABLE_NAME DEF_PCT_INCREASE
------------ ----------------
TABLE_W_PART 30

_______________________________________________________________________
Prepared by M.A.Asad Page 138 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Handling of compress option on partitioned indexes

Different usages of the COMPRESS option usage with partitioning indexes, from
8.1.X version until 9.0.1.

Usage of COMPRESS option with Partitioning indexes

I. General Presentation

The COMPRESS option has been introduced since the 8.1.5 version of Oracle. It offers
the means to load the key indexes with less space used in leaf blocks of indexes. It is
very useful when the indexes are growing using huge amount of space, and you want to
save space on your disks.

II. Partitioning method

II.1 RANGE PARTITIONING

We will now consider the following partitioned table:

CREATE TABLE emp_range


(empno NUMBER(4) NOT NULL,
ename VARCHAR2(30),
hiredate date,
sal NUMBER(7,2))
PARTITION BY RANGE(EMPNO)
(partition emp_p1 VALUES LESS THAN (50),
partition emp_p2 VALUES LESS THAN (100),
partition emp_p3 VALUES LESS THAN (150),
partition emp_p4 VALUES LESS THAN (MAXVALUE));

Local Indexes

SQL> create unique index idx_empno_ename_emp_range on emp_range(empno, ename)


local compress;

Index created.

We can now verify the creation of the Index in Compressed mode:

SQL> select INDEX_NAME, UNIQUENESS, COMPRESSION, PREFIX_LENGTH


from user_indexes
2 where index_name = UPPER('idx_empno_ename_emp_range');

INDEX_NAME UNIQUENES COMPRESS PREFIX_LENGTH


------------------------------ --------- -------- -------------
IDX_EMPNO_ENAME_EMP_RANGE UNIQUE ENABLED 1

_______________________________________________________________________
Prepared by M.A.Asad Page 139 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

By default, the first column has been choosen as the prefix entry in each leaf block index.

We can also verify it at the partition level:

SQL> select INDEX_NAME, PARTITION_NAME, COMPRESSION from


user_ind_partitions
2 where index_name = UPPER('idx_empno_ename_emp_range')
3 order by partition_position;

INDEX_NAME PARTITION_NAME COMPRESS


------------------------------ ------------------------------ --------
IDX_EMPNO_ENAME_EMP_RANGE EMP_P1 ENABLED
IDX_EMPNO_ENAME_EMP_RANGE EMP_P2 ENABLED
IDX_EMPNO_ENAME_EMP_RANGE EMP_P3 ENABLED
IDX_EMPNO_ENAME_EMP_RANGE EMP_P4 ENABLED

In 8i version, the compress option can be used only during the Index creation phase.
If you try to modify it later, you will receive the following errors:

SQL> alter index IDX_EMPNO_ENAME_EMP_RANGE nocompress;


alter index IDX_EMPNO_ENAME_EMP_RANGE nocompress
*
ERROR at line 1:
ORA-02243: invalid ALTER INDEX or ALTER SNAPSHOT option

or

SQL> alter index IDX_EMPNO_ENAME_EMP_RANGE rebuild partition EMP_P1


nocompress;
alter index IDX_EMPNO_ENAME_EMP_RANGE rebuild partition EMP_P1
nocompress
*
ERROR at line 1:
ORA-14010: this physical attribute may not be specified for an index partition

The only possible solution will be then to recreate the partitioned index:

SQL> drop index idx_empno_ename_emp_range;

Index dropped.

SQL> create unique index idx_empno_ename_emp_range on emp_range(empno, ename)


local;
Index created.
SQL> select INDEX_NAME, PARTITION_NAME, COMPRESSION from
user_ind_partitions
2 where index_name = UPPER('idx_empno_ename_emp_range')
3 order by partition_position;

_______________________________________________________________________
Prepared by M.A.Asad Page 140 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

INDEX_NAME PARTITION_NAME COMPRESS


------------------------------ ------------------------------ --------
IDX_EMPNO_ENAME_EMP_RANGE EMP_P1 DISABLED
IDX_EMPNO_ENAME_EMP_RANGE EMP_P2 DISABLED
IDX_EMPNO_ENAME_EMP_RANGE EMP_P3 DISABLED
IDX_EMPNO_ENAME_EMP_RANGE EMP_P4 DISABLED

The same rules may be applied with non-unique partitioned indexes.

You can also decide to set the compress option on some specific partitions only as
following:

SQL8i> create unique index idx_empno_ename_emp_range on emp_range(empno,


ename) compress local
2 ( partition P1 compress,
3 partition P2 nocompress,
4 partition P3 compress,
5* partition P4 nocompress);

Index created.

SQL8i> select INDEX_NAME, PARTITION_NAME, COMPRESSION from


user_ind_partitions
2 where index_name = UPPER('idx_empno_ename_emp_range')
3* order by partition_position;

INDEX_NAME PARTITION_NAME COMPRESS


------------------------------ ------------------------------ --------
IDX_EMPNO_ENAME_EMP_RANGE P1 ENABLED
IDX_EMPNO_ENAME_EMP_RANGE P2 DISABLED
IDX_EMPNO_ENAME_EMP_RANGE P3 ENABLED
IDX_EMPNO_ENAME_EMP_RANGE P4 DISABLED

The compress keyword must be set both at the highest level in the creation order and on
each partition definition.

Global indexes
The same rules can be applied for Global partitioned indexes.

SQL> create index idx_global_sal_emp_range on emp_range(sal)


2 global
3 partition by range(sal)
4 (partition P1 values less than (10000),
5 partition P2 values less than (50000),
6 partition P3 values less than (MAXVALUE)) compress;

_______________________________________________________________________
Prepared by M.A.Asad Page 141 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Index created.

SQL> select INDEX_NAME, PARTITION_NAME, COMPRESSION from


user_ind_partitions
2 where index_name = UPPER('idx_global_sal_emp_range')
3 order by partition_position;

INDEX_NAME PARTITION_NAME COMPRESS


------------------------------ ------------------------------ --------
IDX_GLOBAL_SAL_EMP_RANGE P1 ENABLED
IDX_GLOBAL_SAL_EMP_RANGE P2 ENABLED
IDX_GLOBAL_SAL_EMP_RANGE P3 ENABLED

II.2 HASH PARTITIONING

CREATE TABLE emp_hash


(empno NUMBER(4) NOT NULL,
ename VARCHAR2(10),
sal NUMBER(7,2))
PARTITION BY HASH(sal)
PARTITIONS 4;

As with RANGE partitioning, the Compress option must be used at the index creation.
To remove this option you have to drop and recreate the entire partitioned index.

SQL> create index idx_ename_emp_hash on emp_hash(ename) local compress;

Index created.

SQL> select INDEX_NAME, PARTITION_NAME, COMPRESSION from


user_ind_partitions
2 where index_name = UPPER('idx_ename_emp_hash')
3 order by partition_position;

INDEX_NAME PARTITION_NAME COMPRESS


------------------------------ ------------------------------ --------
IDX_ENAME_EMP_HASH SYS_P13473 ENABLED
IDX_ENAME_EMP_HASH SYS_P13474 ENABLED
IDX_ENAME_EMP_HASH SYS_P13475 ENABLED
IDX_ENAME_EMP_HASH SYS_P13476 ENABLED

It will also be usable on local and global indexes.

Further, the compress option cannot be specified at partition level as the only usable
parameter which can be set with Hash is the associated tablespace.

II.3 COMPOSITE PARTITIONING

_______________________________________________________________________
Prepared by M.A.Asad Page 142 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

CREATE TABLE emp_composite


(empno NUMBER(4) NOT NULL,
ename VARCHAR2(10),
hiredate DATE,
sal NUMBER(6))
PARTITION BY RANGE(empno)
SUBPARTITION BY HASH(sal) SUB-PARTITIONS 4
(PARTITION p1 VALUES LESS THAN (50),
PARTITION p2 VALUES LESS THAN (100),
PARTITION p3 VALUES LESS THAN (150),
PARTITION p4 VALUES LESS THAN (MAXVALUE));

If you try to create a compress index, you will receive:

SQL9i> create index idx_loc_empno_ename_emp_comp on emp_composite(empno,


ename) local compress;
create index idx_loc_empno_ename_emp_comp on emp_composite(empno, ename)
local compress
*
ERROR at line 1:
ORA-08113: composite partition index may not be compressed

NB:

You can create global index only if it is RANGE PARTITIONED.


SQL9i> create index idx_global_emp_comp on emp_composite(sal)
2 global
3 partition by range(sal)
4 (partition P1 values less than (10000),
5 partition P2 values less than (50000),
6 partition P3 values less than (MAXVALUE)) compress;
Index created.
II.4 LIST PARTITIONING (only in 9i version)

CREATE TABLE emp_list


(empno NUMBER(4) NOT NULL,
ename VARCHAR2(10),
hiredate date,
zone VARCHAR2(10),
sal NUMBER(7,2))
PARTITION BY LIST (zone)
(PARTITION northwest VALUES ('OR', 'WA'),
PARTITION southwest VALUES ('AZ', 'UT'),
PARTITION q1_northeast VALUES ('NY', 'VM', 'NJ'),
PARTITION q1_southeast VALUES ('FL', 'GA'),
PARTITION q1_northcentral VALUES ('SD', 'WI'),
PARTITION q1_southcentral VALUES ('NM', 'TX'));

SQL9i> create index idx_loc_emp_list on emp_list(empno, ename) local compress;

_______________________________________________________________________
Prepared by M.A.Asad Page 143 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Index created.

SQL9i> create unique index idx_loc_unique_emp_list on emp_list(empno, zone) local


compress;
Index created.

SQL9i> create index idx_global_emp_list on emp_list(sal)


2 global
3 partition by range(sal)
4 (partition P1 values less than (10000),
5 partition P2 values less than (50000),
6* partition P3 values less than (MAXVALUE)) compress;

Index created.

SQLP9i> select index_name, partition_name, COMPRESSION from user_ind_partitions


2 where index_name = UPPER('idx_global_emp_list')
3* order by partition_position;

INDEX_NAME PARTITION_NAME COMPRESS


------------------------------ ------------------------------ --------
IDX_GLOBAL_EMP_LIST P1 ENABLED
IDX_GLOBAL_EMP_LIST P2 ENABLED
IDX_GLOBAL_EMP_LIST P3 ENABLED

Since the 9.0.1 version, we can now reverse the storage algorithm used for each partition:

SQL9i> alter index idx_global_emp_list rebuild partition P1 nocompress;


Index altered.
SQL9i> select index_name, partition_name, COMPRESSION from user_ind_partitions
2 where index_name = UPPER('idx_global_emp_list')
3 order by partition_position;

INDEX_NAME PARTITION_NAME COMPRESS


------------------------------ ------------------------------ --------
IDX_GLOBAL_EMP_LIST P1 DISABLED
IDX_GLOBAL_EMP_LIST P2 ENABLED
IDX_GLOBAL_EMP_LIST P3 ENABLED

II.5 IOT table

You can use the compress option as with non-partitioned IOT.

CREATE TABLE EMP_IOT


(EMPNO NUMBER UNIQUE,
ENAME VARCHAR2(10),
JOB VARCHAR2(9),
MGR NUMBER(4),
HIREDATE DATE,
SAL NUMBER(7,2),

_______________________________________________________________________
Prepared by M.A.Asad Page 144 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
COMM NUMBER(7,2),
DEPTNO NUMBER,
PRIMARY KEY (ENAME, EMPNO))
ORGANIZATION INDEX
partition by range (empno)
( partition P1 values less than (1000),
partition P2 values less than (2000),
partition P3 values less than (MAXVALUE)) compress;

Table created.

You can specify this option at the partition level:

SQL8i> CREATE TABLE EMP_IOT


2 (EMPNO NUMBER UNIQUE,
3 ENAME VARCHAR2(10),
4 JOB VARCHAR2(9),
5 MGR NUMBER(4),
6 HIREDATE DATE,
7 SAL NUMBER(7,2),
8 COMM NUMBER(7,2),
9 DEPTNO NUMBER,
10 PRIMARY KEY (ENAME, EMPNO))
11 ORGANIZATION INDEX compress
12 partition by range (empno)
13 ( partition P1 values less than (1000) compress,
14 partition P2 values less than (2000) nocompress
15* partition P3 values less than (MAXVALUE));

Table created.

It can now be used in 9i with HASH partitioning:

SQL9i> CREATE TABLE EMP_IOT


2 (EMPNO NUMBER UNIQUE,
3 ENAME VARCHAR2(10),
4 JOB VARCHAR2(9),
5 MGR NUMBER(4),
6 HIREDATE DATE,
7 SAL NUMBER(7,2),
8 COMM NUMBER(7,2),
9 DEPTNO NUMBER,
10 PRIMARY KEY (ENAME, EMPNO))
11 ORGANIZATION INDEX
12* partition by hash(empno) partitions 4 compress;

Table created.

You can check this compression status by the following order:

_______________________________________________________________________
Prepared by M.A.Asad Page 145 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL9i>select index_name, partition_name, compression from user_ind_partitions
2 where index_name like 'SYS_IOT_TOP_32226'
3 order by partition_position;

INDEX_NAME PARTITION_NAME COMPRESS


------------------------------ ------------------------------ --------
SYS_IOT_TOP_32226 SYS_P17 ENABLED
SYS_IOT_TOP_32226 SYS_P18 ENABLED
SYS_IOT_TOP_32226 SYS_P19 ENABLED
SYS_IOT_TOP_32226 SYS_P20 ENABLED

II.6 Bitmap Indexes

The compress option is unavailable with bitmap indexes in 8i and also in 9i.

SQL9i>create bitmap index emp_bitmap on emp_range(empno) local compress;


create bitmap index emp_bitmap on emp_range(empno) local compress
*
ERROR at line 1:
ORA-02158: invalid CREATE INDEX option

Partition pruning in 9iR2--multicolumn partition keys


To delineate specific circumstances under which pruning takes place for partition tables
using more than one column as a partition key.

Partition Pruning with multicolumn partition keys

Examples of tables partitioned on a single column, and partition pruning based on these,
are easy to find and this article will not deal with them save for a brief explanatory
example.

For more information on basic single column partition pruning, see the Concepts Guide
and documents listed at the end of this article. This article is intended to detail the
behavior of pruning with multicolumn keys.

PARTITION PRUNING CONCEPTS

Partition pruning, or partition elimination, refers to the ability of the Cost Based
Optimizer to generate a query plan that only searches partitions containing data that will
be returned by the query. For example, if a table FOO is partitioned on column BAR,
and the partition key for BAR is partitioned into ranges <less than 2>, <less than 4>,
<less than 6> etc., a query with a clause ...WHERE BAR = 5... will search only the third
partition. In some cases, a join such as ...WHERE FOO.BAR = FOO2.BAR may be
eligible for pruning as well.

Some tables are partitioned based on more than one column. An example would be:

CREATE TABLE FOO


(BAR1 NUMBER,

_______________________________________________________________________
Prepared by M.A.Asad Page 146 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
BAR2 NUMBER
)
PARTITION BY RANGE (BAR1, BAR2)
(
PARTITION FIRST VALUES LESS THAN (2,2),
PARTITION SECOND VALUES LESS THAN (2,4),
PARTITION THIRD VALUES LESS THAN (2,6),
PARTITION FOURTH VALUES LESS THAN (4,2),
etc....
)

In a case like this, pruning is a bit more complex. We need to consider two cases.

In the case of a single table predicate, such as ...WHERE BAR1 = 5..., a query will
exhibit pruning provided the prefix (leading edge) of the partition key (at minimum) is in
the where clause. BAR2 is not sufficient for pruning as it is not the leading edge. Plans
for future versions are to allow pruning based on ANY column of the partition key, but
which version is still indefinite.

In the case of a join predicate, however, ALL columns of the partition key must be
present and joined for pruning to take place. Example:

CREATE TABLE FOO2


(BAR1 NUMBER,
BAR2 NUMBER,
BAR3 NUMBER
)

...WHERE FOO.BAR1=FOO2.BAR1 AND FOO.BAR2=FOO2.BAR2 and FOO2.BAR3


= 3...
will allow pruning to take place.

But

...WHERE FOO.BAR1=FOO2.BAR1 AND FOO.BAR2= 5


will not because neither condition is satisfied. Both columns are not present AND joined,
and the leading edge is not present in the single table predicate.

In the case of partitioning based on 3 or more columns, you can have a multicolumn
"prefix", and pruning will be done based on that for single table predicates. For example,
if you have a 4 column partition key, and in the where clause you have:

...where col1 = 5
and col2 = 42
and col5 = 3.14

pruning will take place based on col1 and col2, as they are a prefix of the key.

Coalesce partition|subpartition option of the ALTER TABLE command

_______________________________________________________________________
Prepared by M.A.Asad Page 147 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
COALESCE PARTITION|SUBPARTITION options of the ALTER TABLE command
===============================================================

The ALTER TABLE COALESCE PARTITION command was introduced in Oracle 8i


and is provided mainly to be the compliment of the ADD partition operation and can be
used when too many partitions exist.

In some cases, COALESCE can be considered a replacement for the MERGE partition
operation, which is not supported for hash partitioning.

1. This applies ONLY to HASH PARTITIONED tables.

2. This clause specifies that Oracle should select a hash partition, distribute its
contents into one of the remaining partitions (determined by the hash function),
and then drop the selected partition.

3. The data from the coalesced partition is only redistributed to a single partition. So
dissolving one of four partitions will result in data skew in a 2:1:1 ratio in the
remaining three partitions.

4. Use this command sparingly.

Example:

SQL> CREATE TABLE orders8hash(


2 ordid NUMBER,
3 orderdate DATE,
4 productid NUMBER,
5 quantity NUMBER)
6 PARTITION BY HASH (orderdate) partitions 8
7 STORE IN(DATA01,DATA02,
DATA03,DATA01,DATA02,DATA03,DATA01,DATA02);
Table created.

Now let's look at USER_TAB_PARTITIONS and USER_OBJECTS:

SQL> select
2 TABLE_NAME,
3 PARTITION_NAME,
4 SUBPARTITION_COUNT,
5 HIGH_VALUE,
6 TABLESPACE_NAME
7 from user_tab_partitions
8 where table_name = 'ORDERS8HASH'
9 ORDER BY PARTITION_NAME;

TABLE_NAME PARTITION_NAME SUBPARTITION_COUNT HIGH_VALUE


TABLESPACE

_______________________________________________________________________
Prepared by M.A.Asad Page 148 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
------------ --------------- ------------------ -------------------- ----------
ORDERS8HASH SYS_P1 0 DATA01
ORDERS8HASH SYS_P2 0 DATA02
ORDERS8HASH SYS_P3 0 DATA03
ORDERS8HASH SYS_P4 0 DATA01
ORDERS8HASH SYS_P5 0 DATA02
ORDERS8HASH SYS_P6 0 DATA03
ORDERS8HASH SYS_P7 0 DATA01
ORDERS8HASH SYS_P8 0 DATA02

8 rows selected.

SQL> select
2 object_name,
3 subobject_name,
4 data_object_id,
5 object_type
6 from user_objects
7 where object_name = 'ORDERS8HASH'
8 order by object_name;

OBJECT_NAME SUBOBJECT_NAME DATA_OBJECT_ID


OBJECT_TYPE
------------ --------------- -------------- -------------------------
ORDERS8HASH SYS_P1 14079 TABLE PARTITION
ORDERS8HASH SYS_P2 14080 TABLE PARTITION
ORDERS8HASH SYS_P3 14081 TABLE PARTITION
ORDERS8HASH SYS_P4 14082 TABLE PARTITION
ORDERS8HASH SYS_P5 14083 TABLE PARTITION
ORDERS8HASH SYS_P6 14084 TABLE PARTITION
ORDERS8HASH SYS_P7 14085 TABLE PARTITION
ORDERS8HASH SYS_P8 14086 TABLE PARTITION
ORDERS8HASH TABLE

9 rows selected.

Let's add a new partition to the orders8hash (hash partitioned) table:

SQL> alter table orders8hash add partition NEW_HASH tablespace DATA02;


Table altered.

SQL> select
2 TABLE_NAME,
3 PARTITION_NAME,
4 SUBPARTITION_COUNT,
5 HIGH_VALUE,
6 TABLESPACE_NAME
7 from user_tab_partitions
8 where table_name = 'ORDERS8HASH'
9 ORDER BY PARTITION_NAME;

_______________________________________________________________________
Prepared by M.A.Asad Page 149 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

TABLE_NAME PARTITION_NAME SUBPARTITION_COUNT HIGH_VALUE


TABLESPACE
------------ --------------- ------------------ -------------------- ----------
ORDERS8HASH NEW_HASH 0 DATA02
ORDERS8HASH SYS_P1 0 DATA01
ORDERS8HASH SYS_P2 0 DATA02
ORDERS8HASH SYS_P3 0 DATA03
ORDERS8HASH SYS_P4 0 DATA01
ORDERS8HASH SYS_P5 0 DATA02
ORDERS8HASH SYS_P6 0 DATA03
ORDERS8HASH SYS_P7 0 DATA01
ORDERS8HASH SYS_P8 0 DATA02

9 rows selected.

Lets coalesce a partition:

SQL> alter table ORDERS8HASH COALESCE partition;


Table altered.

SQL> select
2 TABLE_NAME,
3 PARTITION_NAME,
4 SUBPARTITION_COUNT,
5 HIGH_VALUE,
6 TABLESPACE_NAME
7 from user_tab_partitions
8 where table_name = 'ORDERS8HASH'
9 ORDER BY PARTITION_NAME;

TABLE_NAME PARTITION_NAME SUBPARTITION_COUNT HIGH_VALUE


TABLESPACE
------------ --------------- ------------------ -------------------- ----------
ORDERS8HASH SYS_P1 0 DATA01
ORDERS8HASH SYS_P2 0 DATA02
ORDERS8HASH SYS_P3 0 DATA03
ORDERS8HASH SYS_P4 0 DATA01
ORDERS8HASH SYS_P5 0 DATA02
ORDERS8HASH SYS_P6 0 DATA03
ORDERS8HASH SYS_P7 0 DATA01
ORDERS8HASH SYS_P8 0 DATA02

8 rows selected.

NOTE: Our table ORDERS8HASH has no data inserted in it. The selection of the
partition to be dissolved (coalesced) is done internally by RDBMS, and will vary in each
case.

_______________________________________________________________________
Prepared by M.A.Asad Page 150 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

The ALTER TABLE MODIFY PARTITION COALESCE SUBPARTITION option can


be used to distribute the contents of a subpartition (selected by RDBMS) of the specified
partition of a table partitioned using the COMPOSITE method into one or more
remaining sub-partitions (determined by the hash function) of the same partition and then
destroy the selected subpartition.

 Subpartition selected for dissolution will be selected based on requirements of the


hash function.

 This option may not be used with tables partitioned using range or hash methods.

How Implement Hash Partitioning on IOT tables in 9i.

How implement Hash partitioning on IOT tables.

I. Background

Since Oracle8i, specifically version 8.1.5, tables built with IOT structure were partitioned
only with the RANGE partitioning option. The partitioning key would be a subset of the
primary key columns defined on the IOT. In case of overflow segment definition, it was
equi-partitioned with IOT table.

For example:
CREATE TABLE emp_iot
(empno NUMBER(4) PRIMARY KEY,
ename VARCHAR2(10),
sal NUMBER(7,2))
ORGANIZATION INDEX INCLUDING ename OVERFLOW
PARTITION BY RANGE(empno)
(partition emp_p1 VALUES LESS THAN (50) TABLESPACE TBS1,
partition emp_p2 VALUES LESS THAN (100) TABLESPACE TBS2,
partition emp_p3 VALUES LESS THAN (150) TABLESPACE TBS3,
partition emp_p4 VALUES LESS THAN (MAXVALUE) tablespace TBS4);

This will create 8 partitioned segments, 4 IOT partitions and 4 IOT overflow segments.

select table_name, iot_name, iot_type, partitioned


from dba_tables
where table_name like '%IOT%';

TABLE_NAME IOT_NAME IOT_TYPE PARTITIONED


-------------------- ---------- ------------ -----------
EMP_IOT IOT YES
SYS_IOT_OVER_12770 EMP_IOT IOT_OVERFLOW YES
II 9i Enhancement

Now in Oracle9i, we can partition IOT table by HASH. The same rules that were
applied on previous version are still valid.

_______________________________________________________________________
Prepared by M.A.Asad Page 151 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

The following example and queries below show this new functionality:

CREATE TABLE emp_iot


(empno NUMBER(4) PRIMARY KEY,
ename VARCHAR2(10),
sal NUMBER(7,2))
ORGANIZATION INDEX INCLUDING ename OVERFLOW
PARTITION BY HASH(empno) partitions 4
STORE IN (USERS);

select table_name, iot_name, iot_type, tablespace_name


from dba_tables
where table_name like '%IOT%';

TABLE_NAME IOT_NAME IOT_TYPE TABLESPACE_NAME


------------------- ------------------- ------------ ----------------
EMP_IOT IOT
SYS_IOT_OVER_5390 EMP_IOT IOT_OVERFLOW

select index_name, table_name, partitioned


from dba_indexes
where index_name like '%IOT%';

INDEX_NAME TABLE_NAME PARTITIONED


------------------- -------------- -----------------
SYS_IOT_TOP_5390 EMP_IOT YES

select table_name, partitioning_type, partition_count


from dba_part_tables
where table_name like '%IOT%';

TABLE_NAME PARTITIONING_TYPE PARTITION_COUNT


-------------------- ----------------- ---------------
EMP_IOT HASH 4
SYS_IOT_OVER_5390 HASH 4

select table_name, partition_position, partition_name,


tablespace_name
from dba_tab_partitions
where table_name like '%IOT%';

TABLE_NAME PARTITION_POSITION PARTITION_NAME


TABLESPACE_NAME
----------------- ------------------ --------------- ---------------
EMP_IOT 1 SYS_P1

_______________________________________________________________________
Prepared by M.A.Asad Page 152 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
EMP_IOT 2 SYS_P2
EMP_IOT 3 SYS_P3
EMP_IOT 4 SYS_P4
SYS_IOT_OVER_5390 1 SYS_P1 USERS
SYS_IOT_OVER_5390 2 SYS_P2 USERS
SYS_IOT_OVER_5390 3 SYS_P3 USERS
SYS_IOT_OVER_5390 4 SYS_P4 USERS

If you don't follow the rule of basing the partitioning key on primary key, the IOT
creation will fail:

CREATE TABLE emp_iot_2


(empno NUMBER(4) PRIMARY KEY,
ename VARCHAR2(10),
sal NUMBER(7,2))
ORGANIZATION INDEX INCLUDING ename OVERFLOW
PARTITION BY HASH(ename)
(PARTITION P1 tablespace tbs1,
PARTITION P2 tablespace tbs2,
PARTITION P3 tablespace tbs3,
PARTITION P4 tablespace tbs4)
SQL> /
PARTITION BY HASH(ename)
*
ERROR at line 6:
ORA-25199: partitioning key of a index-organized table must be a subset of the primary
key

If you try also to create a partitioned IOT with LOB column types, you can only use the
RANGE partitioning method:

CREATE TABLE emp_iot_2


(empno NUMBER(4) PRIMARY KEY,
ename VARCHAR2(10),
sal NUMBER(7,2),
image BLOB)
ORGANIZATION INDEX INCLUDING ename OVERFLOW
PARTITION BY HASH(empno)
(PARTITION P1 tablespace tbs1,
PARTITION P2 tablespace tbs2,
PARTITION P3 tablespace tbs3,
PARTITION P4 tablespace tbs4)
SQL> /
CREATE TABLE emp_iot_2
*
ERROR at line 1:
ORA-25182: feature not currently available for index-organized tables

Now, by using the Range partition method:

_______________________________________________________________________
Prepared by M.A.Asad Page 153 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
CREATE TABLE emp_iot_2
(empno NUMBER(4) PRIMARY KEY,
ename VARCHAR2(10),
sal NUMBER(7,2),
image BLOB)
ORGANIZATION INDEX INCLUDING ename OVERFLOW
PARTITION BY RANGE(empno)
(PARTITION P1 values less than (10) tablespace tbs1,
PARTITION P2 values less than (20) tablespace tbs2,
PARTITION P3 values less than (50) tablespace tbs3,
PARTITION P4 values less than (MAXVALUE) tablespace tbs4)
SQL> /

Table created.

Further, when you update the primary key of an IOT table, it will likely move the
corresponding row in a different Hash partition. Then, it will be better to add the
ENABLE ROW MOVEMENT on the IOT definition. This option is disabled by default.

1.0 List Partition Maintenance on Table - Examples

Example 1.1 ( CREATE a List Partitioned Table)

SQL> CREATE TABLE employees_reg_p


2 ( employee_id NUMBER(6)
3 , first_name VARCHAR2(20)
4 , last_name VARCHAR2(25) CONSTRAINT emp_last_name_nn_p NOT NULL
5 , email VARCHAR2(25) CONSTRAINT emp_email_nn_p NOT NULL
6 , phone_number VARCHAR2(20)
7 , hire_date DATE CONSTRAINT emp_hire_date_nn_p NOT NULL
8 , job_id VARCHAR2(10) CONSTRAINTemp_job_nn_p NOT NULL
9 , salary NUMBER(8,2) CONSTRAINT emp_salary_nn_p NOT NULL
10 , commission_pct NUMBER(2,2)
11 , manager_id NUMBER(8)
12 , department_id NUMBER(4)
13 , region VARCHAR2(15)
14 , CONSTRAINT emp_salary_min_p CHECK (salary > 0)) PCTFREE 60
15 partition BY LIST (REGION)
16 (partition Zone_1 VALUES('R1','R10','R11','R12') TABLESPACE example,
17 partition Zone_2 VALUES('R13','R14','R15','R16') TABLESPACE example,
18 partition Zone_3 VALUES('R17','R18','R19','R2') TABLESPACE example,
19 partition Zone_4 VALUES('R20','R21','R22','R23') TABLESPACE example,
20 partition Zone_5 VALUES('R24','R25','R26','R27') TABLESPACE example,
21 partition Zone_6 VALUES('R28','R29','R3','R30') TABLESPACE example,
22 partition Zone_7 VALUES ('R31','R32','R4','R5') TABLESPACE example,
23 partition Zone_8 VALUES ('R6','R7','R8','R9') TABLESPACE example);

Table created.

_______________________________________________________________________
Prepared by M.A.Asad Page 154 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

SQL> SELECT table_name, partition_name partition, high_value,


2 partition_position position FROM user_tab_partitions WHERE
3 table_name='EMPLOYEES_REG_P'
4 order by position;

TABLE_NAME PARTITION HIGH_VALUE POSITION


--------------- ---------- --------------------------------- --------
EMPLOYEES_REG_P ZONE_1 'R1', 'R10', 'R11', 'R12' 1
EMPLOYEES_REG_P ZONE_2 'R13', 'R14', 'R15', 'R16' 2
EMPLOYEES_REG_P ZONE_3 'R17', 'R18', 'R19', 'R2' 3
EMPLOYEES_REG_P ZONE_4 'R20', 'R21', 'R22', 'R23' 4
EMPLOYEES_REG_P ZONE_5 'R24', 'R25', 'R26', 'R27' 5
EMPLOYEES_REG_P ZONE_6 'R28', 'R29', 'R3', 'R30' 6
EMPLOYEES_REG_P ZONE_7 'R31', 'R32', 'R4', 'R5' 7
EMPLOYEES_REG_P ZONE_8 'R6', 'R7', 'R8', 'R9' 8

8 rows selected.

SQL> select table_name, PARTITIONING_TYPE, PARTITION_COUNT from


user_part_tables
2 where table_name = 'EMPLOYEES_REG_P';

TABLE_NAME PARTITIONING_TYPE PARTITION_COUNT


-------------------- -------------------- ---------------
EMPLOYEES_REG_P LIST 8

Example 1.2 ( MODIFY a List Partition )

You must previously delete the rows containing the partitioning key before dropping the
corresponding key. If it's not the case, you will receive the message below:

ORA-14518: partition contains rows corresponding to values being dropped

SQL> Delete employees_reg_p WHERE region in ('R32', 'R22');

68 rows deleted.

SQL> ALTER TABLE employees_reg_p modify partition zone_4


2 DROP values ('R22');

Table altered.

SQL> ALTER TABLE employees_reg_p modify partition zone_7


2 DROP values ('R32');

Table altered.

TABLE_NAME PARTITION HIGH_VALUE POSITION


-------------------- ---------- ------------------------- ----------

_______________________________________________________________________
Prepared by M.A.Asad Page 155 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
EMPLOYEES_REG_P ZONE_1 'R1', 'R10', 'R11', 'R12' 1
EMPLOYEES_REG_P ZONE_2 'R13', 'R14', 'R15', 'R16 2
EMPLOYEES_REG_P ZONE_3 'R17', 'R18', 'R19', 'R2' 3
EMPLOYEES_REG_P ZONE_4 'R20', 'R21', 'R23' 4
EMPLOYEES_REG_P ZONE_5 'R24', 'R25', 'R26', 'R27 5
EMPLOYEES_REG_P ZONE_6 'R28', 'R29', 'R3', 'R30' 6
EMPLOYEES_REG_P ZONE_7 'R31', 'R4', 'R5' 7
EMPLOYEES_REG_P ZONE_8 'R6', 'R7', 'R8', 'R9' 8

8 rows selected.

You cannot DROP all the literal values in a specific partition. If you try it, you will
receive:

ORA-14317: cannot drop the last value of partition

You can also ADD values on a targeted partition.

Example 1.3 ( ADD a List Partition )

SQL> ALTER TABLE employees_reg_p ADD partition ZONE_ADD


2 values ('R22', 'R32');

SQL> SELECT table_name, partition_name partition, high_value,


2 partition_position position FROM user_tab_partitions WHERE
3 table_name='EMPLOYEES_REG_P';

TABLE_NAME PARTITION HIGH_VALUE POSITION


--------------- ---------- --------------------------------- --------
EMPLOYEES_REG_P ZONE_ADD 'R22', 'R32' 9
EMPLOYEES_REG_P ZONE_1 'R1', 'R10', 'R11', 'R12' 1
EMPLOYEES_REG_P ZONE_2 'R13', 'R14', 'R15', 'R16' 2
EMPLOYEES_REG_P ZONE_3 'R17', 'R18', 'R19', 'R2' 3
EMPLOYEES_REG_P ZONE_4 'R20', 'R21', 'R23' 4
EMPLOYEES_REG_P ZONE_5 'R24', 'R25', 'R26', 'R27' 5
EMPLOYEES_REG_P ZONE_6 'R28', 'R29', 'R3', 'R30' 6
EMPLOYEES_REG_P ZONE_7 'R31', 'R4', 'R5' 7
EMPLOYEES_REG_P ZONE_8 'R6', 'R7', 'R8', 'R9' 8

9 rows selected.

SQL> ALTER TABLE employees_reg_p modify partition ZONE_ADD


2 DROP values ('R22');

Table altered.

SQL> ALTER TABLE employees_reg_p modify partition ZONE_4


2 ADD values ('R22');

Table altered.

_______________________________________________________________________
Prepared by M.A.Asad Page 156 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

New In 9.2.0

Restriction on adding list partitions:

You cannot add a list partition if you have already defined a DEFAULT partition for the
table. Instead you must use the split_table_partition clause to split the DEFAULT
partition.

Examlpe :

SQL> alter table sales_by_region add partition region_aiman values ('DC');


alter table sales_by_region add partition region_aiman values ('DC')
*
ERROR at line 1:
ORA-14323: cannot add partition when DEFAULT partition exists

Example 1.4 ( MERGE List Partitions )

You can merge any two partitions. They don't need to be adjacent as with
the RANGE partitioning method, because there is no order to comply.

SQL> ALTER TABLE employees_reg_p merge partitions zone_add, zone_7 into


2 partition zone_7;

Table altered.

SQL> SELECT table_name, partition_name partition, high_value,


2 partition_position position FROM user_tab_partitions WHERE
3 table_name='EMPLOYEES_REG_P'
4 ;

TABLE_NAME PARTITION HIGH_VALUE POSITION


-------------------- ---------- ------------------------- ----------
EMPLOYEES_REG_P ZONE_1 'R1', 'R10', 'R11', 'R12' 1
EMPLOYEES_REG_P ZONE_2 'R13', 'R14', 'R15', 'R16' 2
EMPLOYEES_REG_P ZONE_3 'R17', 'R18', 'R19', 'R2' 3
EMPLOYEES_REG_P ZONE_4 'R20', 'R21', 'R23', 'R22' 4
EMPLOYEES_REG_P ZONE_5 'R24', 'R25', 'R26', 'R27' 5
EMPLOYEES_REG_P ZONE_6 'R28', 'R29', 'R3', 'R30' 6
EMPLOYEES_REG_P ZONE_7 'R32', 'R31', 'R4', 'R5' 7
EMPLOYEES_REG_P ZONE_8 'R6', 'R7', 'R8', 'R9' 8

8 rows selected.

Example 1.5 ( RENAME a List Partitions )

_______________________________________________________________________
Prepared by M.A.Asad Page 157 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL> ALTER TABLE employees_reg_p rename partition zone_7 to zone_merge;

Table altered.

SQL> SELECT table_name, partition_name partition, high_value,


2 partition_position position FROM user_tab_partitions WHERE
3 table_name='EMPLOYEES_REG_P';

TABLE_NAME PARTITION HIGH_VALUE POSITION


--------------- ---------- --------------------------------- --------
EMPLOYEES_REG_P ZONE_1 'R1', 'R10', 'R11', 'R12' 1
EMPLOYEES_REG_P ZONE_2 'R13', 'R14', 'R15', 'R16' 2
EMPLOYEES_REG_P ZONE_3 'R17', 'R18', 'R19', 'R2' 3
EMPLOYEES_REG_P ZONE_4 'R20', 'R21', 'R23', 'R22' 4
EMPLOYEES_REG_P ZONE_5 'R24', 'R25', 'R26', 'R27' 5
EMPLOYEES_REG_P ZONE_6 'R28', 'R29', 'R3', 'R30' 6
EMPLOYEES_REG_P ZONE_MERGE 'R32', 'R31', 'R4', 'R5' 7
EMPLOYEES_REG_P ZONE_8 'R6', 'R7', 'R8', 'R9' 8

8 rows selected.

Example 1.6 ( SPLIT a List Partition )

SQL> ALTER TABLE employees_reg_p merge partitions zone_1, zone_3 into


2 partition zone_merge1_3;

Table altered.

SQL> SELECT table_name, partition_name partition, high_value,


2 partition_position position FROM user_tab_partitions WHERE
3 table_name='EMPLOYEES_REG_P';

TABLE_NAME PARTITION HIGH_VALUE POSITION


--------------- --------------- --------------------------- ----------
EMPLOYEES_REG_P ZONE_2 'R13', 'R14', 'R15', 'R16' 1
EMPLOYEES_REG_P ZONE_4 'R20', 'R21', 'R23', 'R22' 3
EMPLOYEES_REG_P ZONE_5 'R24', 'R25', 'R26', 'R27' 4
EMPLOYEES_REG_P ZONE_6 'R28', 'R29', 'R3', 'R30' 5
EMPLOYEES_REG_P ZONE_MERGE 'R32', 'R31', 'R4', 'R5' 6
EMPLOYEES_REG_P ZONE_8 'R6', 'R7', 'R8', 'R9' 7
EMPLOYEES_REG_P ZONE_MERGE1_3 'R1', 'R10', 'R11', 'R12', 2
'R17', 'R18', 'R19', 'R2'

7 rows selected.

SQL> ALTER TABLE employees_reg_p split partition ZONE_MERGE1_3


2 values ('R17', 'R18', 'R19', 'R2')
3 into ( partition ZONE_3, partition ZONE_1);

_______________________________________________________________________
Prepared by M.A.Asad Page 158 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Table altered.

SQL> SELECT table_name, partition_name partition, high_value,


2 partition_position position FROM user_tab_partitions WHERE
3 table_name='EMPLOYEES_REG_P';

TABLE_NAME PARTITION HIGH_VALUE POSITION


--------------- ---------- --------------------------------- --------
EMPLOYEES_REG_P ZONE_3 'R17', 'R18', 'R19', 'R2' 2
EMPLOYEES_REG_P ZONE_1 'R1', 'R10', 'R11', 'R12' 3
EMPLOYEES_REG_P ZONE_2 'R13', 'R14', 'R15', 'R16' 1
EMPLOYEES_REG_P ZONE_4 'R20', 'R21', 'R23', 'R22' 4
EMPLOYEES_REG_P ZONE_5 'R24', 'R25', 'R26', 'R27' 5
EMPLOYEES_REG_P ZONE_6 'R28', 'R29', 'R3', 'R30' 6
EMPLOYEES_REG_P ZONE_MERGE 'R32', 'R31', 'R4', 'R5' 7
EMPLOYEES_REG_P ZONE_8 'R6', 'R7', 'R8', 'R9' 8

8 rows selected.

The partitioning key values load on the second partition is the result of the subtraction of
the first new values list from the original values list.

Example 1.7 ( TRUNCATE a List Partition )

SQL> CREATE TABLE exchange_zone_3 as select * from employees_reg_p


where 1 = 2;

SQL> INSERT INTO exchange_zone_3 SELECT * FROM employees_reg_p


PARTITION
2 (ZONE_3);

48 rows created.

SQL> ALTER TABLE employees_reg_p TRUNCATE PARTITION ZONE_3 DROP


STORAGE;

Table truncated.

Example 1.8 ( EXCHANGE a List Partition )

SQL> ALTER TABLE employees_reg_p EXCHANGE PARTITION ZONE_3


2 WITH TABLE exchange_zone_3
3 WITHOUT VALIDATION;

Table altered.

Example 1.9 (MOVE a List Partition)

_______________________________________________________________________
Prepared by M.A.Asad Page 159 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL> ALTER TABLE employees_reg_p MOVE PARTITION ZONE_3
TABLESPACE users;

TABLE_NAME PARTITION_NAME TABLESPACE_NAME


PARTITION_POSITION
--------------- -------------------- ---------------- ------------------
EMPLOYEES_REG_P ZONE_2 EXAMPLE 1
EMPLOYEES_REG_P ZONE_3 USERS 2
EMPLOYEES_REG_P ZONE_1 EXAMPLE 3
EMPLOYEES_REG_P ZONE_4 EXAMPLE 4
EMPLOYEES_REG_P ZONE_5 EXAMPLE 5
EMPLOYEES_REG_P ZONE_6 EXAMPLE 6
EMPLOYEES_REG_P ZONE_MERGE EXAMPLE 7
EMPLOYEES_REG_P ZONE_8 EXAMPLE 8

8 rows selected.

Example 1.10 (MODIFY DEFAULT ATTRIBUTES)

It modifies only the clauses which are inherited from the table or tablespace level, for
example, the PCTFREE clause which is set at 60.

SQL> select TABLE_NAME, PARTITIONING_TYPE, DEF_PCT_FREE from


user_part_tables
where table_name = 'EMPLOYEES_REG_P';

TABLE_NAME PARTITIONING_TYPE DEF_PCT_FREE


--------------- -------------------- ------------
EMPLOYEES_REG_P LIST 60

SQL> alter table EMPLOYEES_REG_P MODIFY DEFAULT ATTRIBUTES


PCTFREE 40;

Table altered.

TABLE_NAME PARTITIONING_TYPE DEF_PCT_FREE


--------------- -------------------- ------------
EMPLOYEES_REG_P LIST 40

Example 1.11 (MODIFY REAL ATTRIBUTES OF LIST PARTITION)

It modifies really the current parameters associated with a specified partition table.

SQL> select table_name, partition_name, pct_free from user_tab_partitions


2 where table_name = 'EMPLOYEES_REG_P' and partition_name = 'ZONE_3';

TABLE_NAME PARTITION_NAME PCT_FREE


--------------- -------------------- ----------
EMPLOYEES_REG_P ZONE_3 10

_______________________________________________________________________
Prepared by M.A.Asad Page 160 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL> alter table EMPLOYEES_REG_P modify partition zone_3 pctfree 20;

Table altered.

SQL> select table_name, partition_name, pct_free from user_tab_partitions


2 where table_name = 'EMPLOYEES_REG_P' and partition_name = 'ZONE_3';

TABLE_NAME PARTITION_NAME PCT_FREE


--------------- -------------------- ----------
EMPLOYEES_REG_P ZONE_3 20

2.0 List Partition Pruning - Examples

The partitioned table must be analyzed for validation of Pruning

SQL> analyze table EMPLOYEES_REG_P compute statistics;

Table analyzed.

Example 2.1 ( FULL SCAN )

SQL> SELECT region from employees_reg_p;

------------------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
------------------------------------------------------------------------------------------
| SELECT STATEMENT | | 808 | 16K| 4| | |
| PARTITION LIST ALL | | | | | 1| 8|
| TABLE ACCESS FULL |EMPLOYEES_REG_P | 808 | 16K| 4| 1| 8|
------------------------------------------------------------------------------------------

Statistics
----------------------------------------------------------
0 recursive calls
16 db block gets
77 consistent gets
0 physical reads
0 redo size
34270 bytes sent via SQL*Net to client
7446 bytes received via SQL*Net from client
55 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
808 rows processed

Note: Notice the logical reads (db block gets + consistent gets). These numbers will help
determine if partition pruning is taking place. Compare the FULL baseline scan of the
entire partition table above to the partition pruning examples below.

_______________________________________________________________________
Prepared by M.A.Asad Page 161 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

Example 2.2 ( EQUALITY )

SQL> SELECT region, employee_id, last_name FROM employees_reg_p


2 WHERE region = 'R13';

------------------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
------------------------------------------------------------------------------------------
| SELECT STATEMENT | | 101 | 505 | 1| | |
| TABLE ACCESS FULL |EMPLOYEES_REG_P | 101 | 505 | 1| 1| 1|
------------------------------------------------------------------------------------------

Statistics
----------------------------------------------------------
7 recursive calls
2 db block gets
12 consistent gets
0 physical reads
0 redo size
4435 bytes sent via SQL*Net to client
1289 bytes received via SQL*Net from client
8 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
101 rows processed

Example 2.3 ( IN-LIST )

SQL> SELECT region, employee_id, last_name FROM employees_reg_p


2 WHERE region IN ('R18', 'R25');

------------------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
------------------------------------------------------------------------------------------
| SELECT STATEMENT | | 2 | 42 | 1| | |
| PARTITION LIST INLIST | | | | |KEY(I) |KEY(I) |
| TABLE ACCESS FULL |EMPLOYEES_REG_P | 2 | 42 | 1 |KEY(I) |
KEY(I) |
------------------------------------------------------------------------------------------

Statistics
----------------------------------------------------------
7 recursive calls
4 db block gets

_______________________________________________________________________
Prepared by M.A.Asad Page 162 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
7 consistent gets
0 physical reads
0 redo size
338 bytes sent via SQL*Net to client
372 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
0 rows processed

Example 2.4 ( RANGE )

SQL> SELECT region, employee_id, last_name FROM employees_reg_p


2 WHERE region >= 'R31';

------------------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
------------------------------------------------------------------------------------------
| SELECT STATEMENT | | 555 | 11K| 2| | |
| PARTITION LIST ITERATOR | | | | | | |
| TABLE ACCESS FULL |EMPLOYEES_REG_P | 555 | 11K| 2| | |
------------------------------------------------------------------------------------------

Statistics
----------------------------------------------------------
7 recursive calls
4 db block gets
21 consistent gets
0 physical reads
0 redo size
8521 bytes sent via SQL*Net to client
2206 bytes received via SQL*Net from client
15 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
202 rows processed

Example 2.5 (INVALID)

SQL> select * from EMPLOYEES_REG_P where region < 'R1';

------------------------------------------------------------------------------------------
| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |
------------------------------------------------------------------------------------------
| SELECT STATEMENT | | 101 | 2K| 1| | |
| PARTITION LIST EMPTY | | | | |INVALID|INVALID|
| TABLE ACCESS FULL |EMPLOYEES_REG_P | 101 | 2K| 1 |INVALID|
INVALID|

_______________________________________________________________________
Prepared by M.A.Asad Page 163 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
------------------------------------------------------------------------------------------

Statistics
----------------------------------------------------------
7 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
855 bytes sent via SQL*Net to client
372 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed

Supported Operations on RANGE-LIST Tables


-----------------------------------------
1) All DMLs operations
2) Add partition or subpartition
3) Drop partition or subpartition
4) Modify partition or subpartition
5) Rename partition or subpartition
6) Exchange partition or subpartition
7) Merge partitions or subpartition
8) Modify default attribute of a table or partition
9) Modify real attribute of a partition or subpartition
10) Add/Drop values in subpartition
11) Set subpartition template
12) Rename partition or subpartition
13) Move subpartition
14) Split partition or subpartition
15) Truncate partition or subpartition

Supported Operations on Indexes

Only btree or bitmap Local Indexes can be of RANGE-LIST type.

The following commands can be applied on it:

1) Modify default attributes for table or partition.


2) Modify real attributes for partition or subpartition
3) Rebuild of index subpartition
4) Rename partition or subpartition

Partition Pruning

Oracle support pruning in the case of range-list partitioning when there is a predicate on
the key of type Like, In-list, Equality or Range.

_______________________________________________________________________
Prepared by M.A.Asad Page 164 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Restrictions

The same known restrictions are still available for Range-List partitioning like the
current limit of 64k overall segments.

Fast Split partitioning in 9iR2

Fast Split Partitioning

1. Introduction

The Split functionality was introduced with the first implementation of partitioning in 8.0
version, on the RANGE method. It was extended in the following versions to the new
modes like LIST, RANGE-HASH and RANGE-LIST except the HASH mode which
don't support it.

From 9.2 version, Oracle introduces an optimization on the SPLIT command. Now,
Oracle is able to discover that one of the resulting partition will be empty, and then it
creates only one empty partition. The previous one will stay untouched. Now, the Global
partitioned indexes will stay valid, without using the UPDATE GLOBAL INDEXES
option, like the twice Local index partitions associated with the new table partitions, as
they are no movement on rows. The operation is much faster.

Some conditions must be verified to be able to use that mechanism:

One of the resulting partitions must be empty.


The non empty resulting partition must use the same storage characteristics that the
original.
When LOB column, the split non-empty LOB segments must use the same storage
values than previously.

We will use a RANGE-HASH partition table to illustrate that behaviour:

SQL> create table TEST_FAST_SPLIT


partition by range(deptno)
subpartition by hash (hiredate)
(partition P1 values less than (maxvalue)
(subpartition SP1 )
)
as select * from scott.emp;

Table created.

We will also create twice indexes on it, one Local and one Global:

SQL> create index test_fast_split_lidx on test_fast_split(empno) local;

Index created.

_______________________________________________________________________
Prepared by M.A.Asad Page 165 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
SQL> create index test_fast_split_gidx on test_fast_split(deptno) global
2 partition by range (deptno)
3 (partition GP1 values less than (20),
4 partition GP2 values less than (maxvalue));

Index created.

We can verify the different now the state of the different objects created with the
following queries:

1 select object_name, SUBOBJECT_NAME "SUB", OBJECT_ID,


DATA_OBJECT_ID, OBJECT_TYPE, STATUS
2 from user_objects where object_name in ('TEST_FAST_SPLIT',
'TEST_FAST_SPLIT_LIDX
3 ','TEST_FAST_SPLIT_GIDX')
4* order by 1,3
SQL> /

OBJECT_NAME SUB OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE


-------------------- ----- ---------- -------------- ------------------
TEST_FAST_SPLIT 28147 TABLE
TEST_FAST_SPLIT P1 28148 TABLE PARTITION
TEST_FAST_SPLIT SP1 28149 28149 TABLE SUBPARTITION
TEST_FAST_SPLIT_GIDX 28153 INDEX
TEST_FAST_SPLIT_GIDX GP1 28154 28154 INDEX PARTITION
TEST_FAST_SPLIT_GIDX GP2 28155 28155 INDEX PARTITION
TEST_FAST_SPLIT_LIDX 28150 INDEX
TEST_FAST_SPLIT_LIDX SP1 28151 28151 INDEX SUBPARTITION
TEST_FAST_SPLIT_LIDX P1 28152 INDEX PARTITION

9 rows selected.

You can verify here the DATA_OBJECT_IDs associated with physical segments created,
SP1 for the Table and Local Partition index,
GP1 and GP2 for the Global partition index.

The different states of partition and subpartition indexes can be also verified:

1 select index_name, partition_name, subpartition_name,


2 status from user_ind_sub-partitions
3* where index_name in ('TEST_FAST_SPLIT_LIDX')
SQL> /

INDEX_NAME PARTITION_NAME SUBPARTITION_NAME


STATUS
-------------------- --------------- ------------------------------ --------
TEST_FAST_SPLIT_LIDX P1 SP1 USABLE

1 select index_name, partition_name, status from user_ind_partitions

_______________________________________________________________________
Prepared by M.A.Asad Page 166 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
2* where index_name in ('TEST_FAST_SPLIT_GIDX')
SQL> /

INDEX_NAME PARTITION_NAME STATUS


------------------------------ ------------------------------ --------
TEST_FAST_SPLIT_GIDX GP1 USABLE
TEST_FAST_SPLIT_GIDX GP2 USABLE

2. What was the behaviour before 9.2 version

We will split now the TEST_FAST_SPLIT table:

SQL> alter table test_fast_split split partition P1 at (50)


2 into (partition P1_A, partition P1_B);

Table altered.

all the deptno values were lower than 50, so the P1_A partition must contain all the rows:

SQL> select count(*) from test_fast_split partition(P1_A);

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

SQL> select count(*) from test_fast_split partition(P1_B);

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

The P1_B partition is empty.

What has arrived for segments now. We can verify it by executing again the previous
query on the USER_OBJECTS view:

1 select object_name, SUBOBJECT_NAME "SUB", OBJECT_ID,


DATA_OBJECT_ID, OBJECT_TYPE
2 , STATUS
3 from user_objects where object_name in ('TEST_FAST_SPLIT',
'TEST_FAST_SPLIT_LIDX'
4 ,'TEST_FAST_SPLIT_GIDX')
5* order by 1,2,3
SQL> /

OBJECT_NAME SUB OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE


-------------------- -------- ---------- -------------- ------------------
TEST_FAST_SPLIT P1_A 28156 TABLE PARTITION
TEST_FAST_SPLIT P1_B 28157 TABLE PARTITION

_______________________________________________________________________
Prepared by M.A.Asad Page 167 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
TEST_FAST_SPLIT SYS_SUBP135 28158 28158 TABLE
SUBPARTITION
TEST_FAST_SPLIT SYS_SUBP136 28159 28159 TABLE
SUBPARTITION
TEST_FAST_SPLIT 28147 TABLE
TEST_FAST_SPLIT_GIDX GP1 28154 28154 INDEX PARTITION
TEST_FAST_SPLIT_GIDX GP2 28155 28155 INDEX PARTITION
TEST_FAST_SPLIT_GIDX 28153 INDEX
TEST_FAST_SPLIT_LIDX P1_A 28162 INDEX PARTITION
TEST_FAST_SPLIT_LIDX P1_B 28163 INDEX PARTITION
TEST_FAST_SPLIT_LIDX SYS_SUBP135 28160 28160 INDEX
SUBPARTITION
TEST_FAST_SPLIT_LIDX SYS_SUBP136 28161 28161 INDEX
SUBPARTITION
TEST_FAST_SPLIT_LIDX 28150 INDEX

If you compare this report with the previous one, you can view that the couple
(OBJECT_ID, DATA_OBJECT_ID) have changed for all the partitions on Table and
Local Index. The Global index partitions keep the same IDs.

When splitting the table, Oracle has removed the old sub-partitions and transfered the
datas in the two new sub-partitions either on the TEST_FAST_SPLIT table and on the
TEST_FAST_SPLIT_LIDX index too. This is a costly operation.

What is now the status of (sub)partition indexes:

1 select index_name, partition_name, subpartition_name,


2 status from user_ind_sub-partitions
3* where index_name in ('TEST_FAST_SPLIT_LIDX')
SQL> /

INDEX_NAME PARTITION_NAME SUBPARTITION_NAME


STATUS
-------------------- --------------- ------------------------------ --------
TEST_FAST_SPLIT_LIDX P1_A SYS_SUBP135 UNUSABLE
TEST_FAST_SPLIT_LIDX P1_B SYS_SUBP136 USABLE

For the local index, only one subpartition becomes unusable, as the SYS_SUBP136 is
empty.

1 select index_name, partition_name, status from user_ind_partitions


2* where index_name in ('TEST_FAST_SPLIT_LIDX', 'TEST_FAST_SPLIT_GIDX')
SQL> /

INDEX_NAME PARTITION_NAME STATUS


------------------------------ ------------------------------ --------
TEST_FAST_SPLIT_GIDX GP1 UNUSABLE
TEST_FAST_SPLIT_GIDX GP2 UNUSABLE

_______________________________________________________________________
Prepared by M.A.Asad Page 168 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
For the Global Index, both partitions are UNUSABLE as the rowids stored have became
invalid, so they should be rebuilt to be available again.

Since the 9.0.1 version of Oracle, a new option 'UPDATE GLOBAL INDEXES' offer the
means to update the Global Index partitions online when maintenance operation occurs
on underlying partition table.
This option updates the rowids into Global Index partitions so it stays valid. For more
information on this option,

9.2 What is occuring since 9.2 version

We can verify again the initial state:

SQL> select object_name, SUBOBJECT_NAME "SUB", OBJECT_ID,


DATA_OBJECT_ID, OBJECT_TYPE
1 from user_objects where object_name in ('TEST_FAST_SPLIT',
'TEST_FAST_SPLIT_LIDX'
2 ,'TEST_FAST_SPLIT_GIDX')
3* order by 1,3

SQL> /

OBJECT_NAME SUB OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE


-------------------- ---------- ---------- -------------- ------------------
TEST_FAST_SPLIT 60505 TABLE
TEST_FAST_SPLIT P1 60506 TABLE PARTITION
TEST_FAST_SPLIT SP1 60507 60507 TABLE SUBPARTITION
TEST_FAST_SPLIT_GIDX 60511 INDEX
TEST_FAST_SPLIT_GIDX GP1 60512 60512 INDEX PARTITION
TEST_FAST_SPLIT_GIDX GP2 60513 60513 INDEX PARTITION
TEST_FAST_SPLIT_LIDX 60508 INDEX
TEST_FAST_SPLIT_LIDX SP1 60509 60509 INDEX SUBPARTITION
TEST_FAST_SPLIT_LIDX P1 60510 INDEX PARTITION

6 rows selected.

SQL> alter table test_fast_split split partition P1 at (50)


2 into (partition P1_A, partition P1_B);

Table altered.

OBJECT_NAME SUB OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE


-------------------- ---------- ---------- -------------- ------------------
TEST_FAST_SPLIT 60505 TABLE
TEST_FAST_SPLIT P1_A 60514 TABLE PARTITION
TEST_FAST_SPLIT P1_B 60515 TABLE PARTITION
TEST_FAST_SPLIT SYS_SUBP23 60516 60507 TABLE SUBPARTITION
TEST_FAST_SPLIT SYS_SUBP24 60517 60517 TABLE SUBPARTITION
TEST_FAST_SPLIT_GIDX 60511 INDEX
TEST_FAST_SPLIT_GIDX GP1 60512 60512 INDEX PARTITION

_______________________________________________________________________
Prepared by M.A.Asad Page 169 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
TEST_FAST_SPLIT_GIDX GP2 60513 60513 INDEX PARTITION
TEST_FAST_SPLIT_LIDX 60508 INDEX
TEST_FAST_SPLIT_LIDX SYS_SUBP23 60518 60509 INDEX
SUBPARTITION
TEST_FAST_SPLIT_LIDX SYS_SUBP24 60519 60519 INDEX
SUBPARTITION
TEST_FAST_SPLIT_LIDX P1_A 60520 INDEX PARTITION
TEST_FAST_SPLIT_LIDX P1_B 60521 INDEX PARTITION

13 rows selected.

We can see now that Oracle has only created one additional segment either for the Table
and Local index. The DATA_OBJECT_ID 60507 and 60509 are still present. the 60517
and 60519 DATA_OBJECT_IDs correspond to the new empty (sub)partition segments.

The state of the twice partition indexes will be the following:

INDEX_NAME PARTITION_NAME SUBPARTITION_NA STATUS


------------------------------ --------------- --------------- --------
TEST_FAST_SPLIT_LIDX P1_A SYS_SUBP23 USABLE
TEST_FAST_SPLIT_LIDX P1_B SYS_SUBP24 USABLE

INDEX_NAME PARTITION_NAME STATUS


------------------------------ --------------- --------
TEST_FAST_SPLIT_GIDX GP1 USABLE
TEST_FAST_SPLIT_GIDX GP2 USABLE

As you can see, Oracle is now able to ensure that Local and Global indexes would be still
in valid state.
The 'UPDATE GLOBAL INDEXES' is not necessary in that case.

To be sure that this mechanism will work, you must split your table with non-empty
(sub)partition using the same storage characteristics that the old one, otherwise Oracle
will be unable to use the previous optimization.

FAQ’s

1. What is a Partitioned View?

Answer
Introduced in Oracle Version 7.3 still available in Oracle8 for backwards compatibility.
VLDB's (very large databases) can achieve better performance with partitioned
Tables/Indexes because Partitioned tables can be easily striped across more than one
datafile. This datafaile can reside on different physical devices under different IO
controllers. Also, the optimizer will use execution plans that eliminate certain partitions
based on the partition keys.

2. What is a Partitioned Table?

Answer

_______________________________________________________________________
Prepared by M.A.Asad Page 170 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
In Oracle8, a table can be divided into partitions based on a range of key values. Each
partition can be operated on independently. For example, a table partition can be
recovered, undergo DML (insert, update, delete) transactions, be analyzed, etc. with out
affecting the other partitions.

3. What are advantages of Partitioned Tables?

Answer
The primary benefit of the partitioning option is ease of maintenance on very large tables
and improved reliability. This is important for both OLTP and data warehousing
environments. Partitioning also provides performance improvements via partition
elimination.

4. What is a Partitioned Index?

Answer
The primary benefit of partitioning an index is better query access plans and improved
reliability.

There are three types of partitioned indexes:


a) Global Prefixed
b) Local Prefixed
c) Local Non-prefixed

5. What is a Local Prefixed/Non-Prefixed index?

Answer
Local indexes are indexes on partitioned tables where the index contains ROWID
pointers that refer to rows in only one partition. Local index partitions usually use the
same partition keys as the partitioned table; this is called equi-partitioning.

Prefixed index: means that the partition key is based on the left most columns
in the index.

Non-Prefixed index: means that the partition key is based on something else than
the left most columns in the index.

6. What is a Global index?

Answer
Global Indexes are partitioned either on the same key than the underlying table but
different ranges either on a different key.

 Oracle only supports prefixed global partitioned indexes.

An index is global prefixed if it is partitioned on the left prefix of the index columns.

7. How to create a partitioned Table?

_______________________________________________________________________
Prepared by M.A.Asad Page 171 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
Answer
Use the new enhanced creates table syntax to specify the partition key(s) and range of
values for each partition. The partition name is optional; if omitted, Oracle generates
automatically the partition names.
For example:
Create a table emp partition on the EMPNO column (partition key)

CREATE TABLE emp


(EMPNO NUMBER(5),
...)
PARTITION BY RANGE(EMPNO)
PARTITION emp_p1 VALUES LESS THAN (2000),
PARTITION emp_p2 VALUES LESS THAN (4000),
PARTITION emp_p3 VALUES LESS THAN (MAXVALUE);

8. What are the restrictions for Partitioned tables?

Answer
a) Datatype Restrictions
Partitioned tables cannot have any columns with LONG or LONG RAW datatypes,
LOB datatypes (BLOB, CLOB, NCLOB, or BFILE), or object types. We expect to
support partitioning of tables that contain LOBs in 8.1.

b) Clusters cannot be partitioned

c) Bitmap Restrictions
The only restriction is that bitmap indexes must be local to the partitioned table-they
cannot be global indexes (See "Index Partitioning" on page Oracle8 8-19 for information
about local indexes.)

d) Optimizer Restrictions
Oracle8 COST BASED Optimizer supports partitions. Rule Base Optimizer is not
"partition aware" and will not support partition elimination. So any application still using
RBO will not gain any performance benefits from using partitioned tables or indexes;
they can, however, make use of ease of administration and availability features of
partitioned tables.

c) Physical Restrictions
Partitioned tables cannot span multiple databases, they must be within one instance.

9. How to manipulate partitions?

Answer
Use Partition-Extended Table Names.

The table specification syntax for the following DML statements may contain an optional
partition specification for non-remote partitioned tables.

Partition-Extended Table Name:

_______________________________________________________________________
Prepared by M.A.Asad Page 172 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
"schema.table PARTITION (part_name)"

Where schema is the schema owner, table is the base table name, PARTITION is an
optional keyword, and partition_name is the name of the partition if the PARTITION
keyword was specified.

The partition-extented table name can be used in the following DML statements:
INSERT
UPDATE
DELETE
LOCK TABLE
SELECT

Q) How can I insert data into a particular partition?


SQL> insert into sales partition (p8) values (7000, 'bcd', 10, 30);

Q) How can I delete data from a particular partition?


SQL> delete from sales partition (p8);

Q) How can I update a particular partition?


SQL> update sales partition (p8) set sales_amount = 20;

Q) How can I select from a particular partition?


SQL> select * from sales PARTITION (Q4);

10. What are the restrictions on partition-extended table names?

Answer
The use of partition-extended table names has the following restrictions:

A. A partition-extended table name cannot refer to a remote schema object.

A partition-extended table name cannot contain a dblink or a synonym, which translates


to a table with a dblink. If you need to use remote partitions, you can create a view at the
remote site, which uses the partition-extended table name syntax and refer to that remote
view.

B. The partition-extended table name syntax is not supported by PL/SQL.

A SQL statement using the partition-extended table name syntax cannot be used in a
PL/SQL block, though it can be used through dynamic SQL via the DBMS_SQL
package. Again, if you need to refer to a partition within a PL/SQL block you can instead
use views which in turn use the partition-extended table name syntax.

C. Only base tables are allowed.

A partition extension must be specified with a base table. No synonyms, views, or any
other schema objects are allowed.

_______________________________________________________________________
Prepared by M.A.Asad Page 173 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
11. What is the difference between Table-Level and Partition-Level Export/Import?

Answer
Understanding Table-Level and Partition-Level Export (Server Utilities)

In table-level Export, an entire partitioned or non-partitioned table, along with its indexes
and other table-dependent objects, is exported. All the partitions of a partitioned table are
exported. (This applies to both direct path Export and conventional path Export.) All
Export modes (full, user, table) support table-level Export.

In partition-level Export, the user can export one or more specified partitions of a table.
Full database and user mode Export do not support partition-level Export; only table
mode Export does. Because incremental Exports (incremental, cumulative, and
complete) can be done only in full database mode, partition-level Export cannot be
specified for incremental exports.

Partition-level Import cannot import a non-partitioned exported table. However, a


partitioned table can be imported from a non-partitioned exported table using table-level
Import. Partition-level Import is legal only if the source table (that is, the table called
table-name at export time) was partitioned and exists in the Export file.

If the partition-name is not a valid partition in the export file, Import generates a
warning.

In all modes, partitioned data is exported in a format such that partitions can be imported
selectively.

TABLES=schema_name: schema owner of the table/to be exported


tables_name: table to be exported/imported
partition-name: indicates that the export is a partition-level Export.

Partition-level Export lets you export one or more specified partitions within a table.

If you use table-name:partition-name, the specified table must be partitioned, and


partition-name must be the name of one of its partitions.

The presence of a table-name:partition-name with the TABLES parameter results in


reading from the Export file only data rows from the specified source partition. If you do
not specify the partition-name, the entire table is used as source. Import issues a warning
if the specified partition is not in the list of partitions in the exported table.

Data exported from one or more partitions can be imported into one or more partitions.
Import inserts rows into partitions based on the partitioning criteria in the import
database.

The following line shows an example of a partition-level Export:

exp system/manager FILE = export.dmp TABLES = (scott.b:px, scott.b:py, mary.c,


d:qb)

_______________________________________________________________________
Prepared by M.A.Asad Page 174 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

In this example, scott.b must be a partitioned table, and px and py must be two of its
partitions. The table denoted by mary.c can be a partitioned or non-partitioned table.
Table d, however, must be a partitioned table, and qb must be one of its partitions.

If the table-name or partition-name for the same table is used redundantly in the
command line, Export aborts with an error. For example, the following partition-level
Export command line with a redundant specification for table sc and partition px of table
sc causes Export to abort with an error:

exp system/manager FILE = export.dmp TABLES = (sc, sc:px, sc)

12. How to convert a partitioned table or view into non-partitioned table?

Answer
Use the alter table EXCHANGE PARTITION syntax to accomplish this. This operation
is very fast as it only updates the data dictionary to indicate that the swap has taken
place. SPLIT PARTITION may also be useful when you are dealing with very large
partitions or partition Views.

create dummy partitioned tables to receive (or be exchanged with) the table or view data

13. How to Partition an non-partitioned table?

Answer
SQL:
1. Create table dummy_t as select with the required partitions
2. Alter table EXCHANGE partition T with dummy_T
3. Drop table T

exp/imp:
Perform the following steps to partition an non-partitioned table:
1.Export the table to save the data.
2.Drop the table from the database.
3.Create the table again with partitions.
4.Import the table data.

14. How to Merge table partitions?

Answer
Export/Import:
Partition-level Export and Import provide a way to merge partitions in the same table,
even though SQL does not explicitly support merging partitions. A DBA can use
partition-level Import to merge a table partition into the next highest partition on the
same system. See "Example 2: Merging Partitions of a Table" on page 2-36 for an
example of merging partitions.

CTAS:
1. Create a temporary table to hold the partition data

_______________________________________________________________________
Prepared by M.A.Asad Page 175 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
2. Drop the partition to be merged
3. Insert into Table (select * from temporary table)
4. Drop temp.

15. How to split a table Partition?

Answer
Partition-level Export and Import do not provide for splitting a partition. For information
on how to split a partition, refer to the Oracle8 Server Administrator's Guide.

16. Can you update a partition key column(s) if the update causes rows to be moved from
one partition to another?

Answer
With 8.0, No. If you try to update a partition key that would cause the row to move from
one partition to another, you will see error message ORA-14402; Updating partition key
column would cause a partition change.

With 8.1, you can update the value of a partition key column

sql>alter table <table_name> enable row movement;

Row Movement is disabled by default on all tables. You may check the status of
ROW_MOVEMENT on dba and user_tables.

17. Are there restrictions on importing into partitions or with NLS character sets?

Answer
Yes.

Importing partitioned tables into a database with a different character set will fail.

E.g. Export from US7ASCII, import to WE8ISO8859P1. Import will fail with:
ORA-14037: partition bound of partition "P_2" is too high

18. What are the effects of exchange on local and global indexes?

Answer
A global index will require rebuilding after an exchange partition. If the INCLUDING
INDEXES clause is used in the exchange, local indexes on the will be exchanged with
the corresponding regular indexes. If the EXCLUDING INDEXES clause is used, all the
local index partitions corresponding to the partition and all the regular indexes on the
exchanged table are marked as unusable and will have to be rebuilt.

Q) What is partitioning?

--> Partitioning allows users to decompose tables and indexes into smaller and more
manageable pieces called partitions.

Q) What kinds of objects can be partitioned?

_______________________________________________________________________
Prepared by M.A.Asad Page 176 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________

--> Oracle8 supports only table and index level partitioning. Clustered tables/indexes and
snapshots are not supported.

Q) What is the relationship between a partition and a segment?

--> Each partition is stored in a separate segment.


--> DBA_SEGMENTS will give an idea of which segment stores which partition.

Q) How can the physical and logical attributes of a partition be defined?

--> Logical attributes: all the partitions of a table/index should contain the same columns
and constraint definitions
--> Physical attributes: each partition can have it's own storage parameters and each
partition can reside in a separate tablespace
--> DBA_TAB_PARTITIONS will list the storage parameters for each partition as well
as give the tablespace name associated with each partition.

Q) How can partitions be defined?

--> There are 4 types of partitioning methods:


Range - maps data based on the range of values of the partitioning key available from 8.0
Hash - uses a hash function to distribute the data into the partitions available from 8.1.7
List - maps data based on list of discrete values that you specify available from 9.0.1
Composite - is a combination of any of the above partitioning methods available from
8.1.7

Q) How are partitions named?

--> When a partition is created, you have the option to specify a name for the partition. If
the name is not specified then Oracle generates a name of the format SYS_Pn for table
level partitions and SYS_Cn for index level partitions where 'n' is an integer that makes
the name unique within the database.

Q) How to create a partitioned table?

--> An example would be as follows:

create table sales(


acct_no number(5) unique,
person varchar2(30) not null,
sales_amount number(8) not null,
week_no number(2) not null)
partition by range(week_no)
(partition p1 values less than(4) tablespace data0,
partition p2 values less than(8) tablespace data1,
partition p3 values less than(12) tablespace data2,

_______________________________________________________________________
Prepared by M.A.Asad Page 177 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
partition p4 values less than(16) tablespace data3,
partition p5 values less than(20) tablespace data4,
partition p6 values less than(24) tablespace data5,
partition p7 values less than(28) tablespace data6,
partition p8 values less than(32) tablespace data7,
partition p9 values less than(36) tablespace data8,
partition p10 values less than(40) tablespace data9,
partition p11 values less than(44) tablespace data10,
partition p12 values less than(48) tablespace data11,
partition p13 values less than(52) tablespace data12);

where week_no = partition column


p1..p13 = partitions in the table for the column week_no
values less than (x) = x is the upper partition bound for that partition
data0..data12 = different tablespaces for the different partitions

Q) How are partition keys allocated to a particular partition?

--> They are allocated based on the upper partition bound for that partition. So if I insert
a row with the values such as:

insert into sales values (1000, 'abc', 10, 30);

--> this row should reside in partition #p8 in tablespace data7 since the partition keys in
that partition should compare less than and NOT equal to the partition bound of that
partition.

Q) How can I select data out of a particular partition in the table?

--> select * from sales partition (p8);

ACCT_NO PERSON SALES_AMOUNT WEEK_NO


---------- ------------------------------ ------------ ----------
1000 abc 10 30

Q) How can I insert data into a particular partition?

--> insert into sales partition (p8) values (7000, 'bcd', 10, 30);

Q) How can I delete data from a particular partition?

--> delete from sales partition (p8);

Q) How can I update a particular partition?

--> update sales partition (p8) set sales_amount = 20;

Q) Can I update the value of a partition key column?

With 8.0

_______________________________________________________________________
Prepared by M.A.Asad Page 178 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
--> No, you will get ora-14402
--> The only way to go about doing this is to manually delete the old value and then
insert the new value.

From 8.1 on
You can update the value of a partition key column

sql>alter table <table_name> enable row movement;

Q) Can I add a partition to a non-partitioned table?

--> No, you cannot add a partition to a non-partitioned table but can add a new partition
to a partitioned table. In order to add a partition to a non-partitioned table you have to
drop and recreate the non-partitioned table as a partitioned table.

Q) Can partitioned tables contain LOB (large object) datatypes?

--> Yes, but the LOB column cannot be specified as a partitioning key column.
See examples below:

SQL> create table lob_tab (


2 a integer,
3 b blob,
4 c clob) partition by range (b)
5 (partition b1 values less than('hello') tablespace data0,
6 partition b2 values less than('world') tablespace data1);
b blob,
*
ERROR at line 3:
ORA-14113: partitioned table cannot have column with LOB datatype

SQL> create table lob_tab (


2 a integer,
3 b char(5),
4 c blob) partition by range (b)
5 (partition b1 values less than('hello') tablespace data0,
6* partition b2 values less than('world') tablespace data1);

Table created.

Q) What data dictionary view will give the storage parameters for the individual
partitions of the partitioned table?

--> DBA_TAB_PARTITIONS

Q) How can I find out the upper partition bound for a partition table?

_______________________________________________________________________
Prepared by M.A.Asad Page 179 of 180
Oracle Partitioning, Complete Reference
_______________________________________________________________________
--> select high_value, partition_position from sys.dba_tab_partitions where table_name
= 'SALES';

Q) How different is OBJECT_ID from DATA_OBJECT_ID in the data dictionary view


DBA_OBJECTS?

--> OBJECT_ID DATA_OBJECT_ID SUBOBJECT_NAME


---------- -------------- ------------------------------
1697 1697 P1
1706 1706 P10
1707 1707 P11
1708 1708 P12
1709 1709 P13
1698 1698 P2
1699 1699 P3
1700 1700 P4
1701 1701 P5
1702 1702 P6
1703 1703 P7

OBJECT_ID DATA_OBJECT_ID SUBOBJECT_NAME


---------- -------------- ------------------------------
1704 1704 P8
1705 1705 P9
1696

From the above results OBJECT_ID is the dictionary object# of the partition.
DATA_OBJECT_ID is the data object# of the partition. The partitioned table does not
have a DATA_OBJECT_ID, only it's individual partitions do. Therefore the unique
object number for this partitioned table is 1696. DATA_OBJECT_ID was introduced in
8.0 to track versions of the same segment (certain operations change the version). It is
used to discover stale ROWIDs and stale undo records.

_______________________________________________________________________
Prepared by M.A.Asad Page 180 of 180

Vous aimerez peut-être aussi