Вы находитесь на странице: 1из 54

What Are Index-Organized Tables?

An index-organized table has a storage organization that is a variant of a primary B-tree. Unlike an ordinary (heap-organized) table whose data is stored as an unordered collection (heap), data for an index-organized table is stored in a B-tree index structure in a primary key sorted manner. Each leaf block in the index structure stores both the key and nonkey columns. The structure of an index-organized table provides the following benefits:

Fast random access on the primary key because an index-only scan is sufficient. And, because there is no separate table storage area, changes to the table data (such as adding new rows, updating rows, or deleting rows) result only in updating the index structure. Fast range access on the primary key because the rows are clustered in primary key order. Lower storage requirements because duplication of primary keys is avoided. They are not stored both in the index and underlying table, as is true with heap-organized tables.

Index-organized tables have full table functionality. They support features such as constraints, triggers, LOB and object columns, partitioning, parallel operations, online reorganization, and replication. And, they offer these additional features:

Key compression Overflow storage area and specific column placement Secondary indexes, including bitmap indexes.

Index-organized tables are ideal for OLTP applications, which require fast primary key access and high availability. Queries and DML on an orders table used in electronic order processing are predominantly primary-key based and heavy volume causes fragmentation resulting in a frequent need to reorganize. Because an indexorganized table can be reorganized online and without invalidating its secondary indexes, the window of unavailability is greatly reduced or eliminated. Index-organized tables are suitable for modeling application-specific index structures. For example, content-based information retrieval applications containing text, image and audio data require inverted indexes that can be effectively modeled using index-organized tables. A fundamental component of an internet search engine is an inverted index that can be modeled using index-organized tables.

Creating Index-Organized Tables


You use the CREATE TABLE statement to create index-organized tables, but you must provide additional information:

An ORGANIZATION table

INDEX

qualifier, which indicates that this is an index-organized

A primary key, specified through a column constraint clause (for a single column primary key) or a table constraint clause (for a multiple-column primary key).

Optionally, you can specify the following:

An OVERFLOW clause, which preserves dense clustering of the B-tree index by enabling the storage of some of the nonkey columns in a separate overflow data segment. A PCTTHRESHOLD value, which, when an overflow segment is being used, defines the maximum size of the portion of the row that is stored in the index block, as a percentage of block size. Rows columns that would cause the row size to exceed this maximum are stored in the overflow segment. The row is broken at a column boundary into two pieces, a head piece and tail piece. The head piece fits in the specified threshold and is stored along with the key in the index leaf block. The tail piece is stored in the overflow area as one or more row pieces. Thus, the index entry contains the key value, the nonkey column values that fit the specified threshold, and a pointer to the rest of the row. An INCLUDING clause, which can be used to specify the nonkey columns that are to be stored in the index block with the primary key.

Example: Creating an Index-Organized Table

The following statement creates an index-organized table:


CREATE TABLE admin_docindex( token char(20), doc_id NUMBER, token_frequency NUMBER, token_offsets VARCHAR2(2000), CONSTRAINT pk_admin_docindex PRIMARY KEY (token, doc_id)) ORGANIZATION INDEX TABLESPACE admin_tbs PCTTHRESHOLD 20 OVERFLOW TABLESPACE admin_tbs2;

This example creates an index-organized table named admin_docindex, with a primary key composed of the columns token and doc_id. The OVERFLOW and PCTTHRESHOLD clauses specify that if the length of a row exceeds 20% of the index block size, then the column that exceeded that threshold and all columns after it are moved to the overflow segment. The overflow segment is stored in the admin_tbs2 tablespace.
Restrictions for Index-Organized Tables

The following are restrictions on creating index-organized tables.


The maximum number of columns is 1000. The maximum number of columns in the index portion of a row is 255, including both key and nonkey columns. If more than 255 columns are required, you must use an overflow segment.

The maximum number of columns that you can include in the primary key is 32. PCTTHRESHOLD must be in the range of 150. The default is 50. All key columns must fit within the specified threshold. If the maximum size of a row exceeds 50% of the index block size and you do not specify an overflow segment, the CREATE TABLE statement fails. Index-organized tables cannot have virtual columns.

Creating Index-Organized Tables that Contain Object Types

Index-organized tables can store object types. The following example creates object type admin_typ, then creates an index-organized table containing a column of object type admin_typ:
CREATE OR REPLACE TYPE admin_typ AS OBJECT (col1 NUMBER, col2 VARCHAR2(6)); CREATE TABLE admin_iot (c1 NUMBER primary key, c2 admin_typ) ORGANIZATION INDEX;

You can also create an index-organized table of object types. For example:
CREATE TABLE admin_iot2 OF admin_typ (col1 PRIMARY KEY) ORGANIZATION INDEX;

Another example, that follows, shows that index-organized tables store nested tables efficiently. For a nested table column, the database internally creates a storage table to hold all the nested table rows.
CREATE TYPE project_t AS OBJECT(pno NUMBER, pname VARCHAR2(80)); / CREATE TYPE project_set AS TABLE OF project_t; / CREATE TABLE proj_tab (eno NUMBER, projects PROJECT_SET) NESTED TABLE projects STORE AS emp_project_tab ((PRIMARY KEY(nested_table_id, pno)) ORGANIZATION INDEX) RETURN AS LOCATOR;

The rows belonging to a single nested table instance are identified by a nested_table_id column. If an ordinary table is used to store nested table columns, the nested table rows typically get de-clustered. But when you use an indexorganized table, the nested table rows can be clustered based on the nested_table_id column.
Choosing and Monitoring a Threshold Value

Choose a threshold value that can accommodate your key columns, as well as the first few nonkey columns (if they are frequently accessed). After choosing a threshold value, you can monitor tables to verify that the value you specified is appropriate. You can use the ANALYZE TABLE ... LIST CHAINED ROWS statement to determine the number and identity of rows exceeding the threshold value.

Using the INCLUDING Clause

In addition to specifying PCTTHRESHOLD, you can use the INCLUDING clause to control which nonkey columns are stored with the key columns. The database accommodates all nonkey columns up to and including the column specified in the INCLUDING clause in the index leaf block, provided it does not exceed the specified threshold. All nonkey columns beyond the column specified in the INCLUDING clause are stored in the overflow segment. If the INCLUDING and PCTTHRESHOLD clauses conflict, PCTTHRESHOLD takes precedence. Note:
Oracle Database moves all primary key columns of an indexed-organized table to the beginning of the table (in their key order) to provide efficient primary keybased access. As an example:
CREATE TABLE admin_iot4(a INT, b INT, c INT, d INT, primary key(c,b)) ORGANIZATION INDEX;

The stored column order is: c b a d (instead of: a b c d). The last primary key column is b, based on the stored column order. The INCLUDINGcolumn can be the last primary key column (b in this example), or any nonkey column (that is, any column after b in the stored column order). The following CREATE TABLE statement is similar to the one shown earlier in "Example: Creating an Index-Organized Table" but is modified to create an index-organized table where thetoken_offsets column value is always stored in the overflow area:
CREATE TABLE admin_docindex2( token CHAR(20), doc_id NUMBER, token_frequency NUMBER, token_offsets VARCHAR2(2000), CONSTRAINT pk_admin_docindex2 PRIMARY KEY (token, doc_id)) ORGANIZATION INDEX TABLESPACE admin_tbs PCTTHRESHOLD 20 INCLUDING token_frequency OVERFLOW TABLESPACE admin_tbs2;

Here, only nonkey columns prior to token_offsets (in this case a single column only) are stored with the key column values in the index leaf block.
Parallelizing Index-Organized Table Creation

The CREATE TABLE...AS SELECT statement enables you to create an index-organized table and load data from an existing table into it. By including the PARALLEL clause, the load can be done in parallel. The following statement creates an index-organized table in parallel by selecting rows from the conventional table hr.jobs:

CREATE TABLE admin_iot3(i PRIMARY KEY, j, k, l) ORGANIZATION INDEX PARALLEL AS SELECT * FROM hr.jobs;

This statement provides an alternative to parallel bulk-load using SQL*Loader.


Using Key Compression

Creating an index-organized table using key compression enables you to eliminate repeated occurrences of key column prefix values. Key compression breaks an index key into a prefix and a suffix entry. Compression is achieved by sharing the prefix entries among all the suffix entries in an index block. This sharing can lead to huge savings in space, allowing you to store more keys in each index block while improving performance. You can enable key compression using the

COMPRESS

clause while:

Creating an index-organized table Moving an index-organized table

You can also specify the prefix length (as the number of key columns), which identifies how the key columns are broken into a prefix and suffix entry.
CREATE TABLE admin_iot5(i INT, j INT, k INT, l INT, PRIMARY KEY (i, j, k)) ORGANIZATION INDEX COMPRESS;

The preceding statement is equivalent to the following statement:


CREATE TABLE admin_iot6(i INT, j INT, k INT, l INT, PRIMARY KEY(i, j, k)) ORGANIZATION INDEX COMPRESS 2;

For the list of values (1,2,3), (1,2,4), (1,2,7), (1,3,5), (1,3,4), (1,4,4) the repeated occurrences of (1,2), (1,3) are compressed away. You can also override the default prefix length used for compression as follows:
CREATE TABLE admin_iot7(i INT, j INT, k INT, l INT, PRIMARY KEY (i, j, k)) ORGANIZATION INDEX COMPRESS 1;

For the list of values (1,2,3), (1,2,4), (1,2,7), (1,3,5), (1,3,4), (1,4,4), the repeated occurrences of 1 are compressed away. You can disable compression as follows:
ALTER TABLE admin_iot5 MOVE NOCOMPRESS;

One application of key compression is in a time-series application that uses a set of time-stamped rows belonging to a single item, such as a stock price. Indexorganized tables are attractive for such applications because of the ability to cluster

rows based on the primary key. By defining an index-organized table with primary key (stock symbol, time stamp), you can store and manipulate time-series data efficiently. You can achieve more storage savings by compressing repeated occurrences of the item identifier (for example, the stock symbol) in a time series by using an index-organized table with key compression.

Maintaining Index-Organized Tables


Index-organized tables differ from ordinary tables only in physical organization. Logically, they are manipulated in the same manner as ordinary tables. You can specify an index-organized table just as you would specify a regular table in INSERT, SELECT, DELETE, and UPDATE statements.
Altering Index-Organized Tables

All of the alter options available for ordinary tables are available for index-organized tables. This includes ADD, MODIFY, and DROP COLUMNS and CONSTRAINTS. However, the primary key constraint for an index-organized table cannot be dropped, deferred, or disabled You can use the ALTER TABLE statement to modify physical and storage attributes for both primary key index and overflow data segments. All the attributes specified prior to the OVERFLOWkeyword are applicable to the primary key index segment. All attributes specified after the OVERFLOW key word are applicable to the overflow data segment. For example, you can set theINITRANS of the primary key index segment to 4 and the overflow of the data segment INITRANS to 6 as follows:
ALTER TABLE admin_docindex INITRANS 4 OVERFLOW INITRANS 6;

You can also alter PCTTHRESHOLD and INCLUDING column values. A new setting is used to break the row into head and overflow tail pieces during subsequent operations. For example, thePCTHRESHOLD and INCLUDING column values can be altered for the admin_docindex table as follows:
ALTER TABLE admin_docindex PCTTHRESHOLD 15 INCLUDING doc_id;

By setting the INCLUDING column to doc_id, all the columns that follow token_frequency and token_offsets, are stored in the overflow data segment. For index-organized tables created without an overflow data segment, you can add an overflow data segment by using the ADD OVERFLOW clause. For example, you can add an overflow segment to table admin_iot3 as follows:
ALTER TABLE admin_iot3 ADD OVERFLOW TABLESPACE admin_tbs2;

Moving (Rebuilding) Index-Organized Tables

Because index-organized tables are primarily stored in a B-tree index, you can encounter fragmentation as a consequence of incremental updates. However, you

can use the ALTER fragmentation.

TABLE...MOVE

statement to rebuild the index and reduce this


admin_docindex:

The following statement rebuilds the index-organized table


ALTER TABLE admin_docindex MOVE;

You can rebuild index-organized tables online using the ONLINE keyword. The overflow data segment, if present, is rebuilt when the OVERFLOW keyword is specified. For example, to rebuild the admin_docindex table but not the overflow data segment, perform a move online as follows:
ALTER TABLE admin_docindex MOVE ONLINE;

To rebuild the admin_docindex table along with its overflow data segment perform the move operation as shown in the following statement. This statement also illustrates moving both the table and overflow data segment to new tablespaces.
ALTER TABLE admin_docindex MOVE TABLESPACE admin_tbs2 OVERFLOW TABLESPACE admin_tbs3;

In this last statement, an index-organized table with a LOB column (CLOB) is created. Later, the table is moved with the LOB index and data segment being rebuilt and moved to a new tablespace.
CREATE TABLE admin_iot_lob (c1 number (6) primary key, admin_lob CLOB) ORGANIZATION INDEX LOB (admin_lob) STORE AS (TABLESPACE admin_tbs2); . . . ALTER TABLE admin_iot_lob MOVE LOB (admin_lob) STORE AS (TABLESPACE admin_tbs3);

Creating Secondary Indexes on Index-Organized Tables


You can create secondary indexes on an index-organized tables to provide multiple access paths. Secondary indexes on index-organized tables differ from indexes on ordinary tables in two ways:

They store logical rowids instead of physical rowids. This is necessary because the inherent movability of rows in a B-tree index results in the rows having no permanent physical addresses. If the physical location of a row changes, its logical rowid remains valid. One effect of this is that a table maintenance operation, such as ALTER TABLE ... MOVE, does not make the secondary index unusable. The logical rowid also includes a physical guess which identifies the database block address at which the row is likely to be found. If the physical guess is correct, a secondary index scan would incur a single additional I/O once the secondary key is found. The performance would be similar to that of a secondary index-scan on an ordinary table.

Unique and non-unique secondary indexes, function-based secondary indexes, and bitmap indexes are supported as secondary indexes on index-organized tables.
Creating a Secondary Index on an Index-Organized Table

The following statement shows the creation of a secondary index on the docindex index-organized table where doc_id and token are the key columns:
CREATE INDEX Doc_id_index on Docindex(Doc_id, Token);

This secondary index allows the database to efficiently process a query, such as the following, the involves a predicate on doc_id:
SELECT Token FROM Docindex WHERE Doc_id = 1;

Maintaining Physical Guesses in Logical Rowids

A logical rowid can include a guess, which identifies the block location of a row at the time the guess is made. Instead of doing a full key search, the database uses the guess to search the block directly. However, as new rows are inserted, guesses can become stale. The indexes are still usable through the primary key-component of the logical rowid, but access to rows is slower. Collect index statistics with the DBMS_STATS package to monitor the staleness of guesses. The database checks whether the existing guesses are still valid and records the percentage of rows with valid guesses in the data dictionary. This statistic is stored in the PCT_DIRECT_ACCESS column of the DBA_INDEXES view (and related views). To obtain fresh guesses, you can rebuild the secondary index. Note that rebuilding a secondary index on an index-organized table involves reading the base table, unlike rebuilding an index on an ordinary table. A quicker, more light weight means of fixing the guesses is to use the ALTER INDEX ... UPDATE BLOCK REFERENCES statement. This statement is performed online, while DML is still allowed on the underlying indexorganized table. After you rebuild a secondary index, or otherwise update the block references in the guesses, collect index statistics again.
Bitmap Indexes

Bitmap indexes on index-organized tables are supported, provided the indexorganized table is created with a mapping table. This is done by specifying the MAPPING TABLE clause in theCREATE TABLE statement that you use to create the index-organized table, or in an ALTER TABLE statement to add the mapping table later.

Analyzing Index-Organized Tables

Just like ordinary tables, index-organized tables are analyzed using the DBMS_STATS package, or the ANALYZE statement.
Collecting Optimizer Statistics for Index-Organized Tables

To collect optimizer statistics, use the

DBMS_STATS

package.

For example, the following statement gathers statistics for the indexorganized countries table in the hr schema:
EXECUTE DBMS_STATS.GATHER_TABLE_STATS ('HR','COUNTRIES');

The DBMS_STATS package analyzes both the primary key index segment and the overflow data segment, and computes logical as well as physical statistics for the table.

The logical statistics can be queried using USER_TABLES, ALL_TABLES or DBA_TABLES. You can query the physical statistics of the primary key index segment using USER_INDEXES, ALL_INDEXES or DBA_INDEXES (and using the primary key index name). For example, you can obtain the primary key index segment physical statistics for the table admin_docindex as follows:
SELECT LAST_ANALYZED, BLEVEL,LEAF_BLOCKS, DISTINCT_KEYS FROM DBA_INDEXES WHERE INDEX_NAME= 'PK_ADMIN_DOCINDEX';

You can query the physical statistics for the overflow data segment using the USER_TABLES, ALL_TABLES or DBA_TABLES. You can identify the overflow entry by searching forIOT_TYPE = 'IOT_OVERFLOW'. For example, you can obtain overflow data segment physical attributes associated with the admin_docindex table as follows:
SELECT LAST_ANALYZED, NUM_ROWS, BLOCKS, EMPTY_BLOCKS FROM DBA_TABLES WHERE IOT_TYPE='IOT_OVERFLOW' and IOT_NAME= 'ADMIN_DOCINDEX';

Using the ORDER BY Clause with Index-Organized Tables


If an ORDER BY clause only references the primary key column or a prefix of it, then the optimizer avoids the sorting overhead, as the rows are returned sorted on the primary key columns. The following queries avoid sorting overhead because the data is already sorted on the primary key:
SELECT * FROM admin_docindex2 ORDER BY token, doc_id; SELECT * FROM admin_docindex2 ORDER BY token;

If, however, you have an ORDER BY clause on a suffix of the primary key column or non-primary-key columns, additional sorting is required (assuming no other secondary indexes are defined).
SELECT * FROM admin_docindex2 ORDER BY doc_id; SELECT * FROM admin_docindex2 ORDER BY token_frequency;

Converting Index-Organized Tables to Regular Tables


You can convert index-organized tables to regular tables using the Oracle import or export utilities, or the CREATE TABLE...AS SELECT statement. To convert an index-organized table to a regular table:

Export the index-organized table data using conventional path. Create a regular table definition with the same definition. Import the index-organized table data, making sure IGNORE=y (ensures that object exists error is ignored).

Create CREATE TABLE <table_name> ( <column_name> <data type and precision>, <column_name> <data type and precision>, CONSTRAINT <constraint_name> PRIMARY KEY (<primary key constraint columns>)) ORGANIZATION INDEX; CREATE TABLE labor_hour ( WORK_DATE DATE, EMPLOYEE_NO VARCHAR2(8), CONSTRAINT pk_labor_hour PRIMARY KEY (work_date, employee_no)) ORGANIZATION INDEX; SELECT table_name, iot_name, iot_type FROM user_tables; DROP TABLE labor_hour; SELECT object_name, original_name, type, related FROM user_recyclebin; FLASHBACK TABLE labor_hour TO BEFORE DROP; SELECT object_name, object_type FROM user_objects ORDER BY 1,2; ALTER INDEX "BIN$jzohHP0LRqusV3X3jtaDRQ==$0" RENAME TO pk_labor_hour; CREATE TABLE <table_name> ( <column_name> <data type and precision>, <column_name> <data type and precision>, CONSTRAINT <constraint_name> PRIMARY KEY (<primary key constraint columns>)) ORGANIZATION INDEX COMPRESS <number_of_columns>; CREATE TABLE compressed_iot (owner, object_id, object_name, CONSTRAINT pk_compressed_iot PRIMARY KEY(owner, object_id, object_name)) ORGANIZATION INDEX COMPRESS 2 AS SELECT owner, object_id, object_name

Simple Create IOT

Index Compressed IOT

FROM all_objects WHERE SUBSTR(object_name,1,1) BETWEEN 'A' AND 'W'; CREATE TABLE <table_name> ( <column_name> <data type and precision>, <column_name> <data type and precision>, CONSTRAINT <constraint_name> PRIMARY KEY (<primary key constraint columns>)) ORGANIZATION INDEX INCLUDING <column_name> OVERFLOW TABLESPACE <tablespace_name>; CREATE TABLE labor_hour ( WORK_DATE DATE, EMPLOYEE_NO VARCHAR2(8), SUMMIT_WORK_ORDER_NO VARCHAR2(7), DASH VARCHAR2(2), CLASS_CODE VARCHAR2(6), PAYCODE VARCHAR2(2), ASSIGNED_CREW_NUMBER VARCHAR2(5), TRANSFER_CREW_NUMBER VARCHAR2(5), REFERENCE_TYPE VARCHAR2(1), REFERENCE_NUMBER VARCHAR2(10), OVERTIME_CODE VARCHAR2(1), SHIFT_DIFFERENTIAL VARCHAR2(1) NOT NULL, HOURS NUMBER(4,2) NOT NULL, MOD_USER_ID VARCHAR2(30) DEFAULT USER, MOD_USER_DATE DATE DEFAULT SYSDATE, CONSTRAINT pk_labor_hour PRIMARY KEY (work_date, employee_no, summit_work_order_no, dash, class_code, paycode, assigned_crew_number, transfer_crew_number, reference_type, reference_number, overtime_code, shift_differential)) ORGANIZATION INDEX INCLUDING hours OVERFLOW TABLESPACE uwdata; CREATE TABLE <table_name> ( <column_name> <data type and precision>, <column_name> <data type and precision>, CONSTRAINT <constraint_name> PRIMARY KEY (<primary key constraint columns>)) ORGANIZATION INDEX INCLUDING <column_name> OVERFLOW TABLESPACE <tablespace_name> PARTITION BY RANGE (<partitioning_column>)

Complex IOT with Including Clause

Complex IOT with Including Clause And Partitioning

(<partition definitions>); -- DDL for the tablespaces required for this demo are [here] CREATE TABLE labor_hour ( WORK_DATE DATE, EMPLOYEE_NO VARCHAR2(8), SUMMIT_WORK_ORDER_NO VARCHAR2(7), DASH VARCHAR2(2), CLASS_CODE VARCHAR2(6), PAYCODE VARCHAR2(2), ASSIGNED_CREW_NUMBER VARCHAR2(5), TRANSFER_CREW_NUMBER VARCHAR2(5), REFERENCE_TYPE VARCHAR2(1), REFERENCE_NUMBER VARCHAR2(10), OVERTIME_CODE VARCHAR2(1), SHIFT_DIFFERENTIAL VARCHAR2(1) NOT NULL, HOURS NUMBER(4,2) NOT NULL, MOD_USER_ID VARCHAR2(30) DEFAULT USER, MOD_USER_DATE DATE DEFAULT SYSDATE, CONSTRAINT pk_labor_hour PRIMARY KEY (work_date, employee_no, summit_work_order_no, dash, class_code, paycode, assigned_crew_number, transfer_crew_number, reference_type, reference_number, overtime_code, shift_differential)) ORGANIZATION INDEX INCLUDING hours OVERFLOW TABLESPACE uwdata PARTITION BY RANGE (work_date) ( PARTITION yr06 VALUES LESS THAN (TO_DATE('01-JAN2007', 'DD-MON-YYYY')) TABLESPACE part1, PARTITION yr07 VALUES LESS THAN (TO_DATE('01-JAN2008', 'DD-MON-YYYY')) TABLESPACE part2, PARTITION yr08 VALUES LESS THAN (TO_DATE('01-JAN2009', 'DD-MON-YYYY')) TABLESPACE part3, PARTITION yr99 VALUES LESS THAN (MAXVALUE) TABLESPACE part4); Mapping Table Clause
Specify MAPPING TABLE to instruct Oracle to create a mapping of local to physical ROWIDs and store them in a heap-organized table. This mapping is needed in order to create a bitmap index on the index-organized table. Oracle creates the mapping table in the same tablespace as its parent index-organized table. You cannot query, perform DML operations on, or modify the storage characteristics of the mapping table.

You cannot specify the mapping_table_clause for a partitioned index-organized table.

Create IOT with mapping table

CREATE TABLE <table_name> ( <column_name> <data_type><precision>, <column_name> <data_type><precision>, CONSTRAINT <constraint_name> (<constraint_column_list)) ORGANIZATION INDEX MAPPING TABLE; CREATE TABLE t ( x INT, y INT, CONSTRAINT pk_t_iot PRIMARY KEY(x)) ORGANIZATION INDEX MAPPING TABLE; col iot_map_table new_val iot_map SELECT 'SYS_IOT_MAP_' || object_id iot_map_table FROM user_objects WHERE object_name = 'T'; desc &iot_map -- as rows are inserted they are mapped to mapping table column SYS_NC_01 format a20 INSERT INTO t VALUES (1, 2); INSERT INTO t VALUES (2, 2); SELECT rowid, a.* FROM &iot_map a; -- on update logical row changes but mapping table row doesn't update t set x = 3 where x = 1; SELECT rowid, a.* FROM &iot_map a; -- create a bitmapped index CREATE BITMAP INDEX bix_t ON t(y);

Create Single-Table Cluster by Hash CREATE CLUSTER <schema_name>.<cluster_name> ( <cluster_key_column_name> <data_type>) PCTFREE <integer> PCTUSED <integer> INITRANS <integer> MAXTRANS <integer> SIZE <integer><K | M | G | T> TABLESPACE <tablespace_name> INDEX SINGLE TABLE HASHKEYS <integer> HASH IS <expression> PARALLEL <integer> <NOWROWDEPENDENCIES | ROWDEPENDENCIES> <CACHE | NOCACHE>; CREATE CLUSTER sthc_si (srvr_id NUMBER(10)) SIZE 1024 SINGLE TABLE HASHKEYS 11 TABLESPACE uwdata; set linesize 121 SELECT cluster_name, tablespace_name, key_size, hashkeys, single_table FROM user_clusters; CREATE TABLE si_hash CLUSTER sthc_si (srvr_id) AS SELECT * FROM serv_inst; SELECT table_name, cluster_name, tablespace_name FROM user_tables; set long 1000000 SELECT dbms_metadata.get_ddl('CLUSTER', 'STHC_SI') FROM dual; EXPLAIN PLAN FOR SELECT srvr_id, COUNT(*) FROM serv_inst WHERE srvr_id = 503 GROUP BY srvr_id; SELECT * FROM TABLE(dbms_xplan.display);

Single Table Hash Cluster

EXPLAIN PLAN FOR SELECT srvr_id, COUNT(*) FROM si_hash WHERE srvr_id = 503 GROUP BY srvr_id; SELECT * FROM TABLE(dbms_xplan.display);

Create Multi-Table Cluster by Hash CREATE CLUSTER <schema_name>.<cluster_name> ( Multi-Table Cluster by <cluster_key_column_name> <data_type> <SORT>) Hash PCTFREE <integer> PCTUSED <integer> INITRANS <integer> MAXTRANS <integer> SIZE <integer><K | M | G | T> TABLESPACE <tablespace_name> INDEX <SINGLE TABLE> HASHKEYS <integer> HASH IS <expression> PARALLEL <integer> <NOWROWDEPENDENCIES | ROWDEPENDENCIES> <CACHE | NOCACHE>; CREATE CLUSTER hcl_srvr_id ( si_clustercol NUMBER(10)) PCTFREE 0 TABLESPACE uwdata HASHKEYS 141 ROWDEPENDENCIES; col object_name format a30 SELECT object_name, object_type FROM user_objects ORDER BY object_type; /* Hashkeys must be a prime number. If it is not Oracle will choose the next prime number higher than the value you entered. */ col tablespace_name format a15

SELECT cluster_name, cluster_type, tablespace_name, hashkeys, dependencies FROM user_clusters; CREATE TABLE cservers ( srvr_id NUMBER(10), network_id NUMBER(10), status VARCHAR2(1), latitude FLOAT(20), longitude FLOAT(20), netaddress VARCHAR2(15)) CLUSTER hcl_srvr_id (srvr_id); CREATE TABLE cserv_inst ( siid NUMBER(10), si_status VARCHAR2(15), type VARCHAR2(5), installstatus VARCHAR2(1), location_code NUMBER(10), custacct_id VARCHAR2(10), srvr_id NUMBER(10), ws_id NUMBER(10)) CLUSTER hcl_srvr_id (srvr_id); col col col col col table_name format a25 cluster_owner format a20 cluster_name format a20 clu_column_name format a20 tab_column_name format a20

SELECT table_name, cluster_name, tablespace_name FROM user_tables; SELECT * FROM user_clu_columns; EXPLAIN PLAN FOR SELECT srvr_id FROM servers s WHERE EXISTS ( SELECT srvr_id FROM serv_inst i WHERE s.srvr_id = i.srvr_id); SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT srvr_id

FROM cservers s WHERE EXISTS ( SELECT srvr_id FROM cserv_inst i WHERE s.srvr_id = i.srvr_id); SELECT * FROM TABLE(dbms_xplan.display); INSERT INTO cservers SELECT * FROM servers; COMMIT; SELECT ora_rowscn, srvr_id, latitude, longitude FROM cservers WHERE srvr_id < 11; UPDATE cservers SET srvr_id = 999 WHERE srvr_id < 11 AND rownum = 1; COMMIT; col ora_rowscn format 999999999999999 SELECT ora_rowscn, srvr_id, latitude, longitude FROM cservers WHERE (srvr_id NOT BETWEEN 11 AND 900) ORDER BY 2;

Create Multi-Table Cluster by Index CREATE CLUSTER <schema_name>. <cluster_name> (


Multi-Table Cluster by <cluster_key_column_name> <data_type> <SORT>) Index PCTFREE <integer>

PCTUSED <integer> INITRANS <integer> MAXTRANS <integer> SIZE <integer><K | M | G | T> TABLESPACE <tablespace_name> INDEX <SINGLE TABLE> HASHKEYS <integer> HASH IS <expression> PARALLEL <integer>

<NOWROWDEPENDENCIES | ROWDEPENDENCIES> <CACHE | NOCACHE>; CREATE CLUSTER sc_srvr_id ( srvr_id NUMBER(10)) SIZE 1024; desc user_clusters set linesize 121 SELECT cluster_name, tablespace_name, hashkeys, degree, single_table FROM user_clusters; CREATE INDEX idx_sc_srvr_id ON CLUSTER sc_srvr_id; SELECT index_name, index_type, tablespace_name FROM user_indexes; CREATE TABLE cservers ( srvr_id NUMBER(10), network_id NUMBER(10), status VARCHAR2(1), latitude FLOAT(20), longitude FLOAT(20), netaddress VARCHAR2(15)) CLUSTER sc_srvr_id (srvr_id); CREATE TABLE cserv_inst ( siid NUMBER(10), si_status VARCHAR2(15), type VARCHAR2(5), installstatus VARCHAR2(1), location_code NUMBER(10), custacct_id VARCHAR2(10), srvr_id NUMBER(10), ws_id NUMBER(10)) CLUSTER sc_srvr_id (srvr_id); SELECT table_name, cluster_name, tablespace_name FROM user_tables; -- load tables with data: Click Here EXPLAIN PLAN FOR

SELECT srvr_id FROM servers s WHERE EXISTS ( SELECT srvr_id FROM serv_inst i WHERE s.srvr_id = i.srvr_id); SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT srvr_id FROM cservers s WHERE EXISTS ( SELECT srvr_id FROM cserv_inst i WHERE s.srvr_id = i.srvr_id); SELECT * FROM TABLE(dbms_xplan.display);

Sorted Hash Cluster CREATE CLUSTER <schema_name>.<cluster_name> ( <cluster_key_column_name> <data_type> <SORT>) PCTFREE <integer> PCTUSED <integer> INITRANS <integer> MAXTRANS <integer> SIZE <integer><K | M | G | T> TABLESPACE <tablespace_name> INDEX HASHKEYS <integer> HASH IS <expression> PARALLEL <integer> <NOWROWDEPENDENCIES | ROWDEPENDENCIES> <CACHE | NOCACHE>; CREATE CLUSTER sorted_hc ( program_id NUMBER(3), line_id NUMBER(10) SORT, delivery_dt DATE SORT) TABLESPACE uwdata HASHKEYS 9 HASH IS program_id;

Sorted Hash Cluster

SELECT cluster_name, tablespace_name, hashkeys FROM user_clusters; CREATE TABLE shc_airplane ( program_id NUMBER(3), line_id NUMBER(10) SORT, delivery_dt DATE SORT, customer_id VARCHAR2(3), order_dt DATE) CLUSTER sorted_hc (program_id, line_id, delivery_dt); CREATE TABLE reg_airplane ( program_id NUMBER(3), line_id NUMBER(10), delivery_dt DATE, customer_id VARCHAR2(3), order_dt DATE) TABLESPACE uwdata; DECLARE pid shc_airplane.program_id%TYPE; datemod NUMBER(10,5); BEGIN pid := 777; FOR i IN 1..999 LOOP SELECT DECODE(pid, 737, 747, 747, 757, 757, 767, 767, 777, 777, 737) INTO pid FROM dual; SELECT ROUND((EXTRACT(SECOND FROM SYSTIMESTAMP) * 1000), -2) / 100 INTO datemod FROM dual; INSERT INTO shc_airplane (program_id, line_id, delivery_dt, order_dt) VALUES (pid, i, SYSDATE+datemod, SYSDATE-datemod); INSERT INTO reg_airplane (program_id, line_id, delivery_dt, order_dt) VALUES (pid, i, SYSDATE+datemod, SYSDATE-datemod); END LOOP;

COMMIT; END; / SELECT program_id, COUNT(*) FROM reg_airplane GROUP BY program_id; SELECT program_id, COUNT(*) FROM shc_airplane GROUP BY program_id; SELECT * FROM reg_airplane WHERE rownum < 11; SELECT * FROM shc_airplane WHERE rownum < 11; exec dbms_stats.gather_table_stats(USER, 'REG_AIRPLANE'); exec dbms_stats.gather_table_stats(USER, 'SHC_AIRPLANE'); EXPLAIN PLAN SET statement_id = 'reg' FOR SELECT program_id FROM reg_airplane WHERE program_id = 757 ORDER BY line_id, delivery_dt; EXPLAIN PLAN SET statement_id = 'shc' FOR SELECT program_id FROM shc_airplane WHERE program_id = 757 ORDER BY line_id, delivery_dt; SELECT * FROM table(DBMS_XPLAN.DISPLAY('PLAN_TABLE','reg','ALL')); SELECT * FROM table(DBMS_XPLAN.DISPLAY('PLAN_TABLE','shc','ALL')); EXPLAIN PLAN SET statement_id = 'cpu' FOR

SELECT program_id FROM shc_airplane WHERE program_id = 757 ORDER BY delivery_dt, line_id; SELECT * FROM table(DBMS_XPLAN.DISPLAY('PLAN_TABLE','cpu','ALL')); conn sh/sh
Sorted Hash Cluster Demo 2

grant select on sales to uwclass; conn uwclass/uwclass CREATE CLUSTER sorted_hc ( prod_id NUMBER, time_id DATE SORT) TABLESPACE users HASHKEYS 97 HASH IS prod_id; CREATE TABLE shc_sales CLUSTER sorted_hc (prod_id, time_id) AS SELECT * FROM sh. sales; exec dbms_stats.gather_schema_stats(USER, CASCADE=>TRUE); EXPLAIN PLAN SET statement_id = 'reg' FOR SELECT prod_id FROM sh.sales WHERE prod_id = 12292 ORDER BY time_id; EXPLAIN PLAN SET statement_id = 'shc' FOR SELECT prod_id FROM shc_sales WHERE prod_id = 12292 ORDER BY time_id; set linesize 121 set pagesize 40 SELECT * FROM table(dbms_xplan.display('PLAN_TABLE','reg','ALL'));

SELECT * FROM table(dbms_xplan.display('PLAN_TABLE','shc','ALL'));

Additional Hash Cluster Options CREATE CLUSTER <cluster_name> ( column_name> <data_type>, <column_name> <data_type> HASHKEYS <integer> HASH IN <hash_expression>); CREATE CLUSTER cl_address (postal_code NUMBER, country_id VARCHAR(2)) HASHKEYS 16 HASH IS MOD(postal_code + country_id, 101);
Create Hash Cluster With Hash Expression SELECT cluster_name, tablespace_name, hashkeys,

degree, single_table FROM user_clusters; desc user_cluster_hash_expressions set long 100000 SELECT cluster_name, hash_expression FROM user_cluster_hash_expressions;

Alter Cluster ALTER CLUSTER <cluster_name> INITRANS <integer>


INITRANS

ALTER CLUSTER icl_person INITRANS 2; ALTER CLUSTER <cluster_name> MAXTRANS <integer>


MAXTRANS

ALTER CLUSTER icl_person MAXTRANS 254;


PCTFREE

ALTER CLUSTER <cluster_name> PCTFREE <integer>

ALTER CLUSTER icl_person PCTFREE 10; ALTER CLUSTER <cluster_name> PCTUSED <integer>
PCTUSED

ALTER CLUSTER icl_person PCTUSED 80;

Drop Cluster DROP CLUSTER <cluster_name>;


Drop an empty cluster

DROP CLUSTER sthc_si;


Drop a cluster with its tables

DROP CLUSTER <cluster_name> INCLUDING TABLES; DROP CLUSTER sthc_si INCLUDING TABLES;

Table Partitions CREATE TABLE hash_part ( prof_history_id NUMBER(10), person_id NUMBER(10) NOT NULL, organization_id NUMBER(10) NOT NULL, record_date DATE NOT NULL, prof_hist_comments VARCHAR2(2000)) PARTITION BY HASH (prof_history_id) PARTITIONS 3 STORE IN (part1, part2, part3); desc hash_part SELECT table_name, tablespace_name, partitioned FROM user_tables; desc user_tab_partitions SELECT partition_name, tablespace_name FROM user_tab_partitions; CREATE TABLE interval_part ( person_id NUMBER(5) NOT NULL, first_name VARCHAR2(30),

Hash Partitioned Table

Interval-Numeric Range Partitioned

Table Also possible are Interval-Hash and Interval-List

last_name VARCHAR2(30)) PARTITION BY RANGE (person_id) INTERVAL (100) STORE IN (uwdata) ( PARTITION p1 VALUES LESS THAN (101)) TABLESPACE uwdata; desc interval_part SELECT table_name, tablespace_name, partitioned FROM user_tables; col high_value format a20 SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions; INSERT INTO interval_part (person_id, first_name, last_name) VALUES (100, 'Dan', 'Morgan'); SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions; INSERT INTO interval_part (person_id, first_name, last_name) VALUES (101, 'Heli', 'Helskyaho'); SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions; INSERT INTO interval_part (person_id, first_name, last_name) VALUES (567, 'Tara', 'Havemeyer'); SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions; CREATE TABLE interval_date ( person_id NUMBER(5) NOT NULL, last_name VARCHAR2(30), dob DATE) PARTITION BY RANGE (dob) INTERVAL (NUMTOYMINTERVAL(1,'MONTH')) STORE IN (uwdata) ( PARTITION p1 VALUES LESS THAN (TO_DATE('2008-03-

Interval-Date Range Partitioned Table

15','YYYY-MM-DD'))); INSERT INTO interval_date (person_id, last_name, dob) VALUES (1, 'Morgan', SYSDATE-365); INSERT INTO interval_date (person_id, last_name, dob) VALUES (2, 'Lofstrom', SYSDATE-365); INSERT INTO interval_date (person_id, last_name, dob) VALUES (3, 'Havemeyer', SYSDATE-200); INSERT INTO interval_date (person_id, last_name, dob) VALUES (4, 'Small', SYSDATE-60); INSERT INTO interval_date (person_id, last_name, dob) VALUES (5, 'Ellison', SYSDATE+60); col partition_name format a14 col tablespace_name format a15 col high_value format a85 SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions; CREATE TABLE list_part ( List Partitioned Table deptno NUMBER(10), (add tablespace deptname VARCHAR2(20), names and change quarterly_sales NUMBER(10,2), table name) state VARCHAR2(2)) PARTITION BY LIST (state) ( PARTITION q1_northwest VALUES TABLESPACE part1, PARTITION q1_southwest VALUES 'NM') TABLESPACE part2, PARTITION q1_northeast VALUES 'NJ') TABLESPACE part1, PARTITION q1_southeast VALUES 'GA') TABLESPACE part2,

('OR', 'WA') ('AZ', 'CA', ('NY', 'VT', ('FL',

PARTITION q1_northcent VALUES ('MN', 'WI') TABLESPACE part1, PARTITION q1_southcent VALUES ('OK', 'TX') TABLESPACE part2); SELECT table_name, tablespace_name, partitioned FROM user_tables; SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions; INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO COMMIT; list_part list_part list_part list_part list_part list_part VALUES VALUES VALUES VALUES VALUES VALUES (10, (20, (10, (20, (10, (20, 'A', 'B', 'A', 'B', 'A', 'B', 1000, 1000, 1000, 1000, 1000, 1000, 'OR'); 'AZ'); 'WA'); 'WA'); 'AZ'); 'CA');

SELECT * FROM list_part; SELECT * FROM list_part PARTITION(q1_northwest); CREATE TABLE range_part ( prof_history_id NUMBER(10), person_id NUMBER(10) NOT NULL, organization_id NUMBER(10) NOT NULL, record_date DATE NOT NULL) PARTITION BY RANGE (record_date) ( PARTITION yr0 VALUES LESS THAN (TO_DATE('01-JAN2007','DD-MON-YYYY')) TABLESPACE part1, PARTITION yr7 VALUES LESS THAN (TO_DATE('01-JAN2008','DD-MON-YYYY')) TABLESPACE part2, PARTITION yr8 VALUES LESS THAN (TO_DATE('01-JAN2009','DD-MON-YYYY')) TABLESPACE part3, PARTITION yr9 VALUES LESS THAN (MAXVALUE) TABLESPACE part4); SELECT table_name, tablespace_name, partitioned FROM user_tables; col part_name format a9 col tbsp_name format a9 SELECT partition_name PART_NAME, tablespace_name

Range Partitioned Table - By Date

TBSP_NAME, high_value FROM user_tab_partitions WHERE table_name = 'RANGE_PART'; INSERT 720); INSERT 360); INSERT 180); INSERT INTO range_part VALUES (1, 1, 1, SYSDATEINTO range_part VALUES (2, 2, 2, SYSDATEINTO range_part VALUES (3, 3, 3, SYSDATEINTO range_part VALUES (4, 4, 4, SYSDATE);

SELECT * FROM range_part; SELECT SELECT SELECT SELECT * * * * FROM FROM FROM FROM range_part range_part range_part range_part PARTITION(yr0); PARTITION(yr7); PARTITION(yr8); PARTITION(yr9);

conn / as sysdba ALTER SESSION SET tracefile_identifier = 'range_part'; ALTER SESSION SET EVENTS '10128 trace name context forever, level 1'; SELECT * FROM uwclass.range_part PARTITION(yr8); ALTER SESSION SET SQL_TRACE=FALSE;

Trace file c:\oracle\product\diag dbms\orabase\orabase race\orabase_ora_2976_range_part.trc Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 Production q With the Partitioning, Oracle Label Security, OLAP, Data Mining, Oracle Database Vault and Real Application Testing options Windows XP Version V5.1 Service Pack 3 CPU : 1 - type 586, 1 Physical Cores Process Affinity : 0x00000000 Memory (Avail/Total): Ph:674M/2038M, Ph+PgF:2777M/3932M, VA:1275M/2047M Instance name: orabase Redo thread mounted by this instance: 1 Oracle process number: 24 Windows thread id: 2976, image: ORACLE.EXE (SHAD) *** *** *** *** *** 2008-12-17 23:24:21.421 SESSION ID:(137.1810) 2008-12-17 23:24:21.421 CLIENT ID:() 2008-12-17 23:24:21.421 SERVICE NAME:(SYS$USERS) 2008-12-17 23:24:21.421 MODULE NAME:(sqlplusw.exe) 2008-12-17 23:24:21.421

*** ACTION NAME:() 2008-12-17 23:24:21.421 partition pruning descriptor: type = 0, level = 1 flags = {single, known, set by parser, partition mapping descriptor: partitioning method = range number of partitions = 4 number of partitioning keys = 1 partitioning columns = (4) partition pruning descriptor: type = 0, level = 1 flags = {single, known, set by parser, partition mapping descriptor: partitioning method = range number of partitions = 4 number of partitioning keys = 1 partitioning columns = (4) partition pruning descriptor: type = 0, level = 1 flags = {single, known, set by parser, partition mapping descriptor: partitioning method = range number of partitions = 4 number of partitioning keys = 1 partitioning columns = (4) partition pruning descriptor: type = 0, level = 1 flags = {single, known, set by parser, partition mapping descriptor: partitioning method = range number of partitions = 4 number of partitioning keys = 1 partitioning columns = (4)

*** 2008-12-17 23:24:42.453 Partition Iterator Information: partition level = PARTITION call time = RUN order = ASCENDING Partition iterator for level 1: iterator = RANGE [2, 2] index = 2 current partition: part# = 2, subp# = 1048576, abs# = 2 *** 2008-12-17 23:25:04.359 Partition Iterator Information: partition level = PARTITION call time = RUN order = ASCENDING Partition iterator for level 1: iterator = RANGE [2, 2] index = 2

current partition: part# = 2, subp# = 1048576, abs# = 2 *** 2008-12-17 23:25:53.437 Partition Iterator Information: partition level = PARTITION call time = RUN order = ASCENDING Partition iterator for level 1: iterator = RANGE [2, 2] index = 2 current partition: part# = 2, subp# = 1048576, abs# = 2

x
Range Partitioned Table - By Alpha

CREATE TABLE students ( student_id NUMBER(6), student_fn VARCHAR2(25), student_ln VARCHAR2(25), PRIMARY KEY (student_id)) PARTITION BY RANGE (student_ln) ( PARTITION student_ae VALUES LESS THAN ('F%') TABLESPACEpart1, PARTITION student_fl VALUES LESS THAN ('M%') TABLESPACEpart2, PARTITION student_mr VALUES LESS THAN ('S%') TABLESPACEpart3, PARTITION student_sz VALUES LESS THAN (MAXVALUE) TABLESPACEpart4); SELECT table_name, tablespace_name, partitioned FROM user_tables; SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions; SELECT MIN(num_rows), MAX(num_rows) FROM all_tables WHERE num_rows IS NOT NULL; CREATE TABLE ref_parent ( table_name VARCHAR2(30), order_date DATE, num_rows NUMBER) PARTITION BY RANGE(num_rows) ( PARTITION num_rows1 VALUES LESS THAN (100) TABLESPACE part1, PARTITION num_rows2 VALUES LESS THAN (1000) TABLESPACE part2, PARTITION num_rows3 VALUES LESS THAN (10000)

Referential Partitioned Table

TABLESPACEpart3, PARTITION num_rows4 VALUES LESS THAN (MAXVALUE) TABLESPACEpart4); ALTER TABLE ref_parent ADD CONSTRAINT pk_ref_parent PRIMARY KEY (table_name) USING INDEX; desc ref_parent SELECT table_name, tablespace_name, partitioned FROM user_tables; SELECT partition_name, tablespace_name FROM user_tab_partitions; CREATE TABLE ref_child ( table_name VARCHAR2(30) NOT NULL, index_name VARCHAR2(30) NOT NULL, CONSTRAINT fk_ref_child_parent FOREIGN KEY(table_name) REFERENCES ref_parent(table_name)) PARTITION BY REFERENCE(fk_ref_child_parent); SELECT table_name, partitioning_type, ref_ptn_constraint_name FROM user_part_tables WHERE table_name LIKE 'REF%'; CREATE TABLE syst_part ( tx_id NUMBER(5), begdate DATE) PARTITION BY SYSTEM ( PARTITION p1 TABLESPACE part1, PARTITION p2 TABLESPACE part2, PARTITION p3 TABLESPACE part3); INSERT INTO syst_part VALUES (1, SYSDATE-10); INSERT INTO syst_part PARTITION (p1) VALUES (1, SYSDATE-10); INSERT INTO syst_part PARTITION (p2) VALUES (2, SYSDATE); INSERT INTO syst_part PARTITION (p3) VALUES (3, SYSDATE+10);

Partition by System

SELECT * FROM syst_part PARTITION(p2); CREATE TABLE vcol_part ( tx_id NUMBER(5), begdate DATE, enddate DATE, staylen NUMBER(5) AS (enddate-begdate)) PARTITION BY RANGE (staylen) INTERVAL (10) STORE IN (uwdata) ( PARTITION p1 VALUES LESS THAN (11)) TABLESPACE uwdata; desc vcol_part SELECT table_name, tablespace_name, partitioned FROM user_tables; col high_value format a20 SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions; desc user_tab_cols SELECT column_name, virtual_column, data_default FROM user_tab_cols WHERE table_name = 'VCOL_PART'; INSERT INTO vcol_part (tx_id, begdate, enddate) VALUES (1, SYSDATE-5, SYSDATE); INSERT INTO vcol_part (tx_id, begdate, enddate) VALUES (2, SYSDATE-10, SYSDATE); INSERT INTO vcol_part (tx_id, begdate, enddate) VALUES (3, SYSDATE-15, SYSDATE); INSERT INTO vcol_part (tx_id, begdate, enddate) VALUES (4, SYSDATE-25, SYSDATE);

Partition by Virtual Column

COMMIT; SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions; EXPLAIN PLAN FOR SELECT * FROM vcol_part; SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT * FROM vcol_part WHERE staylen < 11; SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT * FROM vcol_part WHERE staylen BETWEEN 11 AND 20; SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT * FROM vcol_part WHERE staylen >= 11; SELECT * FROM TABLE(dbms_xplan.display);

Composite Partitions CREATE TABLE composite_rng_hash (


Composite Partitioned cust_id NUMBER(10), Table - By Range And cust_name VARCHAR2(25), Hash amount_sold NUMBER(10,2),

time_id DATE) PARTITION BY RANGE(time_id) SUBPARTITION BY HASH(cust_id) SUBPARTITION TEMPLATE( SUBPARTITION sp1 TABLESPACE part1, SUBPARTITION sp2 TABLESPACE part2, SUBPARTITION sp3 TABLESPACE part3, SUBPARTITION sp4 TABLESPACE part4) (

PARTITION sales_pre05 VALUES LESS THAN (TO_DATE('01/01/2005','DD/MM/YYYY')), PARTITION sales_2005 VALUES LESS THAN(TO_DATE('01/01/2006','DD/MM/YYYY')), PARTITION sales_2006 VALUES LESS THAN(TO_DATE('01/01/2007','DD/MM/YYYY')), PARTITION sales_2007 VALUES LESS THAN(TO_DATE('01/01/2008','DD/MM/YYYY')), PARTITION sales_2008 VALUES LESS THAN(TO_DATE('01/01/2009','DD/MM/YYYY')), PARTITION sales_future VALUES LESS THAN(MAXVALUE)); set linesize 121 col table_name format a20 SELECT table_name, partitioned, secondary FROM user_tables; desc user_tab_partitions col partition_name format a15 col spc format 99999 col high_value format a50 SELECT table_name, partition_name, composite, subpartition_count SPC, high_value FROM user_tab_partitions; desc user_tab_subpartitions col subpartition_name format a20 SELECT table_name, partition_name, subpartition_name, subpartition_position FROM user_tab_subpartitions; desc user_subpartition_templates col high_bound format a20 SELECT subpartition_name, tablespace_name, high_bound

FROM user_subpartition_templates; conn sh/sh GRANT select ON sales TO uwclass; GRANT select on customers TO uwclass; conn uwclass/uwclass INSERT INTO composite_rng_hash SELECT c.cust_id, c.cust_first_name || ' ' || c.cust_last_name, s.amount_sold, s.time_id FROM sh.sales s, sh.customers c WHERE s.cust_id = c.cust_id AND rownum < 250001; exec dbms_stats.gather_table_stats(USER, 'COMPOSITE_RNG_HASH', 'SALES_PRE05'); exec dbms_stats.gather_table_stats(USER, 'COMPOSITE_RNG_HASH', 'SALES_2005'); exec dbms_stats.gather_table_stats(USER, 'COMPOSITE_RNG_HASH', 'SALES_2006'); exec dbms_stats.gather_table_stats(USER, 'COMPOSITE_RNG_HASH', 'SALES_2007'); exec dbms_stats.gather_table_stats(USER, 'COMPOSITE_RNG_HASH', 'SALES_2008'); exec dbms_stats.gather_table_stats(USER, 'COMPOSITE_RNG_HASH', 'SALES_FUTURE'); SELECT table_name, partition_name, num_rows FROM user_tab_partitions; SELECT table_name, partition_name, subpartition_name, num_rows FROM user_tab_subpartitions; exec dbms_stats.gather_table_stats(USER, 'COMPOSITE_RNG_HASH', GRANULARITY=>'ALL'); SELECT table_name, partition_name, subpartition_name, num_rows FROM user_tab_subpartitions; set long 1000000 select dbms_metadata.get_ddl('TABLE', 'COMPOSITE_RNG_HASH');

Composite Partitioned cust_id Table - By Range And cust_name List cust_state

CREATE TABLE composite_rng_list ( NUMBER(10), VARCHAR2(25), VARCHAR2(2), time_id DATE) PARTITION BY RANGE(time_id) SUBPARTITION BY LIST (cust_state) SUBPARTITION TEMPLATE( SUBPARTITION west VALUES ('OR', 'WA') TABLESPACE part1, SUBPARTITION east VALUES ('NY', 'CT') TABLESPACE part2, SUBPARTITION cent VALUES ('OK', 'TX') TABLESPACE part3) ( PARTITION per1 VALUES LESS THAN (TO_DATE('01/01/2000','DD/MM/YYYY')), PARTITION per2 VALUES LESS THAN (TO_DATE('01/01/2005','DD/MM/YYYY')), PARTITION per3 VALUES LESS THAN (TO_DATE('01/01/2010','DD/MM/YYYY')), PARTITION future VALUES LESS THAN(MAXVALUE)); desc composite_rng_list SELECT table_name, partition_name, composite, high_value FROM user_tab_partitions; SELECT table_name, partition_name, subpartition_name, num_rows FROM user_tab_subpartitions; CREATE TABLE composite_rng_rng ( NUMBER(10), VARCHAR2(25), VARCHAR2(2), time_id DATE) PARTITION BY RANGE(time_id) SUBPARTITION BY RANGE (cust_id) SUBPARTITION TEMPLATE( SUBPARTITION original VALUES LESS THAN (1001) TABLESPACE part1, SUBPARTITION acquired VALUES LESS THAN (8001) TABLESPACE part2, SUBPARTITION recent VALUES LESS THAN (MAXVALUE) TABLESPACE part3) ( PARTITION per1 VALUES LESS THAN (TO_DATE('01/01/2000','DD/MM/YYYY')),

Composite Partitioned cust_id Table - By Range And cust_name Range cust_state

PARTITION per2 VALUES LESS THAN (TO_DATE('01/01/2005','DD/MM/YYYY')), PARTITION per3 VALUES LESS THAN (TO_DATE('01/01/2010','DD/MM/YYYY')), PARTITION future VALUES LESS THAN (MAXVALUE)); desc composite_rng_rng SELECT table_name, partition_name, composite, high_value FROM user_tab_partitions; SELECT table_name, partition_name, subpartition_name, num_rows FROM user_tab_subpartitions; CREATE TABLE composite_list_hash ( NUMBER(10), VARCHAR2(25), VARCHAR2(2), time_id DATE) PARTITION BY LIST(cust_state) SUBPARTITION BY HASH (cust_id) SUBPARTITION TEMPLATE( SUBPARTITION sp1 TABLESPACE part1, SUBPARTITION sp2 TABLESPACE part2, SUBPARTITION sp3 TABLESPACE part3, SUBPARTITION sp4 TABLESPACE part4) ( PARTITION west VALUES ('OR', 'WA'), PARTITION east VALUES ('NY', 'CT'), PARTITION cent VALUES ('IL', 'MN')); CREATE TABLE composite_list_list ( NUMBER(10), VARCHAR2(25), VARCHAR2(2), time_id DATE) PARTITION BY LIST(cust_state) SUBPARTITION BY LIST (cust_id) SUBPARTITION TEMPLATE( SUBPARTITION beg VALUES (1,3,5) TABLESPACE part1, SUBPARTITION mid VALUES (2,4,6) TABLESPACE part2, SUBPARTITION end VALUES (7,8,9,0) TABLESPACE part3) ( PARTITION west VALUES ('OR', 'WA'), PARTITION east VALUES ('NY', 'CT'), PARTITION cent VALUES ('IL', 'MN'));

Composite Partitioned cust_id Table - By List And cust_name Hash cust_state

Composite Partitioned cust_id Table - By List And cust_name List cust_state

Composite Partitioned cust_id Table - By List And cust_name Range cust_state

CREATE TABLE composite_list_rng ( NUMBER(10), VARCHAR2(25), VARCHAR2(2), time_id DATE) PARTITION BY LIST(cust_state) SUBPARTITION BY RANGE (cust_id) SUBPARTITION TEMPLATE( SUBPARTITION original VALUES LESS THAN (1001) TABLESPACE part1, SUBPARTITION acquired VALUES LESS THAN (8001) TABLESPACE part2, SUBPARTITION recent VALUES LESS THAN (MAXVALUE) TABLESPACE part3) ( PARTITION west VALUES ('OR', 'WA'), PARTITION east VALUES ('NY', 'CT'), PARTITION cent VALUES ('IL', 'MN'));

Compressed Partitions Need syntax diagram


Partition Level Compression

CREATE TABLE sales ( saleskey NUMBER, quarter NUMBER, product NUMBER, salesperson NUMBER, amount NUMBER(12,2), region VARCHAR2(10)) COMPRESS PARTITION BY LIST (region) ( PARTITION northwest VALUES ('NORTHWEST'), PARTITION southwest VALUES ('SOUTHWEST'), PARTITION northeast VALUES ('NORTHEAST') NOCOMPRESS, PARTITION southeast VALUES ('SOUTHEAST'), PARTITION western VALUES ('WESTERN')); SELECT table_name, tablespace_name, partitioned, compression FROM user_tables; SELECT partition_name, tablespace_name, high_value, compression FROM user_tab_partitions;

Alter Table For Partitions ALTER TABLE <table_name> MOVE PARTITION <partition_name> TABLESPACE <tablespace_name>; SELECT table_name, partition_name, tablespace_name FROM user_tab_partitions; ALTER TABLE hash_part MOVE PARTITION sys_p26 TABLESPACE uwdata; ALTER TABLE list_part MOVE PARTITION q1_southcent TABLESPACE uwdata NOLOGGING; ALTER TABLE range_part MOVE PARTITION yr0 TABLESPACE uwdata; ALTER TABLE composite_rng_hash MOVE PARTITION sales_pre98 TABLESPACE uwdata; SELECT table_name, partition_name, tablespace_name FROM user_tab_partitions; ALTER TABLE <table_name>
Moving Subpartitions MOVE SUBPARTITION <subpartition_name>

Moving Partitions Not composite

TABLESPACE <tablespace_name>; SELECT partition_name, subpartition_name, tablespace_name FROM user_tab_subpartitions WHERE TABLE_NAME = 'COMPOSITE_RNG_HASH'; ALTER TABLE composite_rng_hash MOVE SUBPARTITION sales_pre98_sp1 TABLESPACE uwdata PARALLEL (DEGREE 2); SELECT partition_name, subpartition_name, tablespace_name FROM user_tab_subpartitions WHERE TABLE_NAME = 'COMPOSITE_RNG_HASH'; ALTER TABLE <table_name>

Merging Subpartitions MERGE SUBPARTITIONS <subpartition_name> List Only

INTO SUBPARTITION <subpartition_name TABLESPACE <tablespace_name>; ALTER TABLE composite_rng_hash MERGE SUBPARTITIONS sales_pre98_sp1, sales_pre98_sp2 INTO SUBPARTITION sales_pre98_sp12 TABLESPACE part1; CREATE TABLE range_list ( cust_id NUMBER(10), channel_id NUMBER(3), amount_sold NUMBER(10,2), time_id DATE) PARTITION BY RANGE(time_id) SUBPARTITION BY LIST(channel_id) SUBPARTITION TEMPLATE( SUBPARTITION sp1 VALUES (2, 3) TABLESPACE SUBPARTITION sp2 VALUES (4, 5) TABLESPACE SUBPARTITION sp3 VALUES (6, 7) TABLESPACE SUBPARTITION sp4 VALUES (8, 9) TABLESPACE (PARTITION sp98 VALUES LESS THAN(TO_DATE('01/01/1998','DD/MM/YYYY')), PARTITION s98 VALUES LESS THAN(TO_DATE('01/01/1999','DD/MM/YYYY')), PARTITION s99 VALUES LESS THAN(TO_DATE('01/01/2000','DD/MM/YYYY')), PARTITION s2K VALUES LESS THAN(TO_DATE('01/01/2001','DD/MM/YYYY')), PARTITION s01 VALUES LESS THAN(TO_DATE('01/01/2002','DD/MM/YYYY')), PARTITION sf VALUES LESS THAN(MAXVALUE)); col high_value format a20 SELECT partition_name, subpartition_name, tablespace_name, high_value FROM user_tab_subpartitions WHERE table_name = 'RANGE_LIST'; ALTER TABLE range_list MERGE SUBPARTITIONS sp98_sp1, sp98_sp2 INTO

part1, part2, part3, part4)

SUBPARTITION sp12 PARALLEL (DEGREE 2) TABLESPACE part1; SELECT partition_name, subpartition_name, tablespace_name, high_value FROM user_tab_subpartitions WHERE table_name = 'RANGE_LIST'; ALTER TABLE <table_name>
Modify A Subpartition SET SUBPARTITION TEMPLATE ( Template SUBPARTITION <subpartition_name>, TABLESPACE

<tablespace_name>, SUBPARTITION <subpartition_name>, TABLESPACE <tablespace_name>); SELECT partition_name, subpartition_name, tablespace_name FROM user_tab_subpartitions WHERE table_name = 'RANGE_LIST'; ALTER TABLE range_list SET SUBPARTITION TEMPLATE ( SUBPARTITION sp1 VALUES (2, SUBPARTITION sp2 VALUES (4, SUBPARTITION sp3 VALUES (6, SUBPARTITION sp4 VALUES (8, SUBPARTITION sp5 VALUES (0,

3) 5) 7) 9) 1)

TABLESPACE TABLESPACE TABLESPACE TABLESPACE TABLESPACE

part1, part2, part3, part4, uwdata);

col partition_name format a15 col high_value format a30 SELECT partition_name, subpartition_name, tablespace_name, high_value FROM user_tab_subpartitions WHERE table_name = 'RANGE_LIST'; ALTER TABLE range_list DROP PARTITION sf; ALTER TABLE range_list ADD PARTITION s02 VALUES LESS THAN(TO_DATE('01/01/2003','DD/MM/YYYY')); ALTER TABLE range_list ADD PARTITION sf VALUES LESS THAN(MAXVALUE);

SELECT partition_name, subpartition_name, tablespace_name, high_value FROM user_tab_subpartitions WHERE table_name = 'RANGE_LIST'; set long 1000000 select dbms_metadata.get_ddl('TABLE', 'RANGE_LIST'); ALTER TABLE <table_name>
Change The MODIFY DEFAULT ATTRIBUTES FOR PARTITION Tablespace Name <partition_name> For A Future Partition TABLESPACE <tablespace_name>;

select partition_name, tablespace_name, high_value from user_tab_partitions where table_name = 'RANGE_LIST'; ALTER TABLE range_list MODIFY DEFAULT ATTRIBUTES FOR PARTITION s98 TABLESPACE part1; select partition_name, tablespace_name, high_value from user_tab_partitions where table_name = 'RANGE_LIST'; SELECT partition_name, subpartition_name, tablespace_name FROM user_tab_subpartitions WHERE table_name = 'RANGE_LIST'; ALTER TABLE <table_name> MODIFY PARTITION <partition_name> ADD VALUES (<values_list>); SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions WHERE table_name = 'LIST_PART'; ALTER TABLE list_part MODIFY PARTITION q1_northcent ADD VALUES ('MI', 'OH'); SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions WHERE table_name = 'LIST_PART'; ALTER TABLE <table_name> MODIFY PARTITION <partition_name>

Modify A List Partitioned List

Drop Values From A

List Partitioned List

DROP VALUES (<values_list>); ALTER TABLE list_part MODIFY PARTITION q1_southwest DROP VALUES ('NM'); SELECT partition_name, tablespace_name, high_value FROM user_tab_partitions WHERE table_name = 'LIST_PART'; ALTER TABLE <table_name> EXCHANGE PARTITION <partition_name> WITH TABLE <new_table_name> <including | excluding> INDEXES <with | without> VALIDATION EXCEPTIONS INTO <schema.table_name>; SELECT table_name, partition_name, num_rows FROM user_tab_partitions WHERE table_name = 'LIST_PART'; CREATE TABLE q1_northwest AS SELECT * FROM list_part WHERE 1=2; SELECT * FROM list_part; SELECT * FROM list_part PARTITION(q1_northwest); ALTER TABLE list_part EXCHANGE PARTITION q1_northwest WITH TABLE q1_northwest INCLUDING INDEXES WITHOUT VALIDATION EXCEPTIONS INTO uwclass.problems; SELECT * FROM q1_northwest; SELECT * FROM list_part; CREATE TABLE range_part ( rid NUMBER, col1 VARCHAR2(10), col2 VARCHAR2(100)) PARTITION BY RANGE(rid) ( partition p1 VALUES LESS THAN (1000), partition p3 VALUES LESS THAN (3000),

Convert A Partition Into A Stand-alone Table

Convert A Standalone Table Into A Partition

partition pm VALUES LESS THAN (MAXVALUE)); CREATE TABLE new_part ( rid NUMBER, col1 VARCHAR2(10), col2 VARCHAR2(100)); INSERT /*+ APPEND ORDERED FULL(s1) USE_NL(s2) */ INTO new_part SELECT 3000 + TRUNC((rownum-1)/500,6), TO_CHAR(rownum), rpad('x',100) FROM sys.source$ s1, sys.source$ s2 WHERE rownum <= 100000; COMMIT; SELECT COUNT(*) FROM range_part; SELECT COUNT(*) FROM new_part; col high_value format a20 SELECT table_name, partition_name, high_value FROM user_tab_partitions; set timing on ALTER TABLE range_part EXCHANGE PARTITION pm WITH TABLE new_part; set timing off DROP TABLE range_part PURGE; DROP TABLE new_part PURGE; -- recreate and populate tables set timing on ALTER TABLE range_part EXCHANGE PARTITION pm WITH TABLE new_part WITHOUT VALIDATION; -- again drop the tables, recreate, and load them -- add some realistic constraints ALTER TABLE range_part ADD CONSTRAINT pk_range_part PRIMARY KEY(rid) USING INDEX LOCAL;

ALTER TABLE new_part ADD CONSTRAINT pk_new_part PRIMARY KEY(rid) USING INDEX; set timing on ALTER TABLE range_part EXCHANGE PARTITION pm WITH TABLE new_part INCLUDING INDEXES WITHOUT VALIDATION; -- repeat again but this time do the following before the exchange ALTER TABLE range_part MODIFY PRIMARY KEY NOVALIDATE; ALTER TABLE new_part MODIFY PRIMARY KEY NOVALIDATE; set timing on ALTER TABLE range_part EXCHANGE PARTITION pm WITH TABLE new_part INCLUDING INDEXES WITHOUT VALIDATION; ALTER TABLE <table_name>
Renaming A Partition RENAME PARTITION <existing_partition_name>

TO <new_partition_name>; SELECT table_name, partition_name FROM user_tab_partitions; ALTER TABLE range_list RENAME PARTITION sf TO sales_future; SELECT table_name, partition_name FROM user_tab_partitions; ALTER TABLE <table_name> SPLIT PARTITION <partition_name> AT <range_definition> INTO (PARTITION <first_partition>, PARTITION <second_partition>) UPDATE GLOBAL INDEXES; SELECT table_name, partition_name, high_value FROM user_tab_partitions WHERE table_name = 'RANGE_PART';

Split Partition

INSERT INTO JAN-1998'), INSERT INTO JAN-1999'), INSERT INTO JAN-2000'), INSERT INTO JAN-2001'), INSERT INTO MAR-2001'), INSERT INTO SEP-2001'), INSERT INTO DEC-2001'), INSERT INTO JAN-2002'), INSERT INTO JAN-2003'), COMMIT;

range_part 'A'); range_part 'A'); range_part 'A'); range_part 'A'); range_part 'A'); range_part 'A'); range_part 'A'); range_part 'A'); range_part 'A');

VALUES (1, 1, 1, TO_DATE('01VALUES (1, 1, 1, TO_DATE('01VALUES (1, 1, 1, TO_DATE('01VALUES (1, 1, 1, TO_DATE('01VALUES (1, 1, 1, TO_DATE('15VALUES (1, 1, 1, TO_DATE('16VALUES (1, 1, 1, TO_DATE('20VALUES (1, 1, 1, TO_DATE('01VALUES (1, 1, 1, TO_DATE('01-

col ph_comments format a10 SELECT * FROM range_part; SELECT * FROM range_part PARTITION(yr2a); ALTER TABLE range_part SPLIT PARTITION yr2 AT (TO_DATE('30-JUN-2001','DD-MON-YYYY')) INTO (PARTITION yr2a, PARTITION yr2b) UPDATE GLOBAL INDEXES; SELECT * FROM range_part PARTITION(yr2a); SELECT * FROM range_part PARTITION(yr2b); SELECT table_name, partition_name, high_value FROM user_tab_partitions WHERE table_name = 'RANGE_PART'; ALTER TABLE <table_name> TRUNCATE PARTITION <partition_name> DROP STORAGE; SELECT * FROM range_part PARTITION(yr2b); ALTER TABLE range_part TRUNCATE PARTITION yr2b DROP STORAGE;

Truncate A Partition

SELECT * FROM range_part PARTITION(yr2b); ALTER TABLE <table_name> Split An LOB Partition SPLIT PARTITION <partition_name> AT <split location> INTO (PARTITION <new_partition_name> TABLESPACE <tablespace_name>" LOB <column_name> STORE AS (TABLESPACE <tablespace_name>), PARTITION <new_partition_name> LOB (<column_name>) STORE AS (TABLESPACE <tablespace_name>); ALTER TABLE print_media_part SPLIT PARTITION p2 AT (150) INTO (PARTITION p2a TABLESPACE omf_ts1 LOB ad_photo, ad_composite) STORE AS (TABLESPACE omf_ts2), PARTITION p2b LOB (ad_photo, ad_composite) STORE AS (TABLESPACE omf_ts2)); ALTER TABLE <table_name> ADD PARTITION <new_partition_name> VALUES LESS THAN (MAXVALUE) LOB (<column_name>) STORE AS (TABLESPACE <tablespace_name); ALTER TABLE print_media_part ADD PARTITION p3 VALUES LESS THAN (MAXVALUE) LOB (ad_photo, ad_composite) STORE AS (TABLESPACE omf_ts2) LOB (ad_sourcetext, ad_finaltext) STORE AS (TABLESPACE omf_ts1);

Add Partition And Specify BLOB/LOB Storage

Index Partitions CREATE INDEX <index_name>


Global Index Creation ON <table_name> <column_name_list>;

SELECT i.index_name, i.composite, i.partition_name, i.high_value FROM user_ind_partitions i, user_tab_partitions t WHERE i.partition_name = t.partition_name AND t.table_name = 'RANGE_PART';

SELECT partition_name FROM user_tab_partitions WHERE table_name = 'RANGE_PART'; CREATE INDEX gi_range_part_person_id ON range_part (person_id); SELECT index_name, partitioned FROM user_indexes WHERE table_name = 'RANGE_PART'; DROP INDEX gi_range_part_person_id; CREATE INDEX <index_name>
Local Index Creation ON <table_name> <column_name_list> LOCAL; and Partition Pruning Demo CREATE INDEX li_range_part_person_id

ON range_part (person_id) LOCAL; SELECT index_name, partitioned FROM user_indexes WHERE table_name = 'RANGE_PART'; SELECT ip.index_name, ip.composite, ip.partition_name, ip.high_value FROM user_ind_partitions ip, user_indexes ui WHERE ip.index_name = ui.index_name AND ui.table_name = 'RANGE_PART'; DROP INDEX li_range_part_person_id; CREATE INDEX li_range_part_person_id ON range_part (person_id) LOCAL ( PARTITION yr0 TABLESPACE part1, PARTITION yr1 TABLESPACE part2, PARTITION yr2a TABLESPACE part3, PARTITION yr2b TABLESPACE part4, PARTITION yr9 TABLESPACE uwdata); col tablespace_name format a15 SELECT ip.index_name, ip.partition_name, ip.tablespace_name, ip.high_value FROM user_ind_partitions ip, user_indexes ui WHERE ip.index_name = ui.index_name

AND ui.table_name = 'RANGE_PART'; SELECT * FROM range_part; SELECT * FROM range_part PARTITION(yr2a); EXPLAIN PLAN FOR SELECT * FROM range_part WHERE record_date BETWEEN TO_DATE('01-JAN-1998') AND TO_DATE('31-JAN-1998'); SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT * FROM range_part WHERE record_date BETWEEN TO_DATE('01-JAN-1998') AND TO_DATE('31-DEC-2000'); SELECT * FROM TABLE(dbms_xplan.display); EXPLAIN PLAN FOR SELECT * FROM range_part WHERE record_date BETWEEN TO_DATE('01-JAN-1999') AND TO_DATE('31-DEC-2002'); SELECT * FROM TABLE(dbms_xplan.display); CREATE INDEX <index_name>
Global Partition Index ON <table_name> <column_name_list> Creation GLOBAL PARTITION BY RANGE

(partition_column_name_list) ( PARTITION <partition_name> VALUES <condition>); DROP INDEX li_range_part_person_id; UPDATE range_part SET organization_id = ROWNUM; col ph_comments format a15 SELECT * FROM range_part; CREATE INDEX gi_range_part_person_id ON range_part (organization_id) GLOBAL PARTITION BY RANGE(organization_id) (

PARTITION p1 VALUES LESS THAN(4) TABLESPACE part1, PARTITION p2 VALUES LESS THAN(MAXVALUE) TABLESPACE part2); col high_value format a20 SELECT ip.index_name, ip.partition_name, ip.tablespace_name, ip.high_value FROM user_ind_partitions ip, user_indexes ui WHERE ip.index_name = ui.index_name AND ui.table_name = 'RANGE_PART';
Query for Unusable Indexes

SELECT index_name, partition_name, status FROM user_ind_partitions;

Alter Table and Index For Partitions ALTER TABLE <table_name> MODIFY PARTITION <partition_name> REBUILD UNUSABLE LOCAL INDEXES; CREATE INDEX li_range_part_person_id ON range_part (person_id) LOCAL; SELECT t.table_name, i.index_name, i.partition_name, i.status FROM user_ind_partitions i, user_tab_partitions t WHERE i.partition_name = t.partition_name; ALTER TABLE range_part MODIFY PARTITION yr0 REBUILD UNUSABLE LOCAL INDEXES; ALTER TABLE <table_name> Rebuild any unusable MODIFY SUBPARTITION <subpartition_name> local index partitions REBUILD UNUSABLE LOCAL INDEXES;
associated with a hash partition at the specific composite partitioned table subpartition level

Rebuild Local All Local Indexes On A Table

SELECT i.table_name, s.index_name, s.partition_name, s.status FROM user_ind_subpartitions s, user_indexes i WHERE s.index_name = i.index_name; ALTER TABLE composite_rng_hash MODIFY SUBPARTITION sales_1999_sp4

REBUILD UNUSABLE LOCAL INDEXES; ALTER INDEX <index_name>


Rebuild (and move) a REBUILD PARTITION <partition_name> local partition index TABLESPACE <new_tablespace_name>;

col partition_name format a10 col tablespace_name format a20 SELECT i.table_name, s.index_name, s.tablespace_name, s.partition_name, s.status FROM user_ind_partitions s, user_indexes i WHERE s.index_name = i.index_name; ALTER INDEX li_range_part_person_id REBUILD PARTITION yr2 TABLESPACE uwdata; SELECT i.table_name, s.index_name, s.tablespace_name, s.partition_name, s.status FROM user_ind_partitions s, user_indexes i WHERE s.index_name = i.index_name;

Drop Partition ALTER TABLE DROP PARTITION <partition_name> [UPDATE GLOBAL INDEXES];
Drop Partition

SELECT table_name, partition_name FROM user_tab_partitions; ALTER TABLE range_list DROP PARTITION s2k UPDATE GLOBAL INDEXES;

Demos conn scott/tiger


Partition Elimination

-- Create a list partitioned table CREATE TABLE partdemo ( empno NUMBER(4) NOT NULL, ename VARCHAR2(10), job VARCHAR2(9), mgr NUMBER(4),

hiredate DATE, sal NUMBER(7, 2), comm NUMBER(7, 2), deptno NUMBER(2)) partition by list(deptno)( partition p1 values (10,30) tablespace uwdata, partition p2 values (20,40) tablespace example); INSERT INTO partdemo SELECT * FROM scott.emp; set linesize 121 SELECT * FROM partdemo; SELECT * FROM partdemo PARTITION(p1); SELECT * FROM partdemo PARTITION(p2); -- Take the example tablespace OFFLINE to examine partition elimination conn / as sysdba ALTER TABLESPACE example OFFLINE; conn scott/tiger SELECT COUNT(*) SELECT COUNT(*) SELECT COUNT(*) AND 19; SELECT COUNT(*) AND 20; SELECT COUNT(*) IN(10,30); FROM partdemo; FROM partdemo WHERE deptno = 10; FROM partdemo WHERE deptno BETWEEN 1 FROM partdemo WHERE deptno BETWEEN 1 FROM partdemo WHERE deptno

conn / as sysdba ALTER TABLESPACE example ONLINE;

Undocumented Partitioning SELECT DISTINCT TBL$OR$IDX$PART$NUM(BBUKDW.VISIT_TX,


Found by Jonathan 0, CALENDAR_DT) Lewis and noted here FROM (

SELECT D.CALENDAR_DT CALENDAR_DT FROM BBUKDW.JPL_DAY D

WHERE D.FINANCIAL_WEEK_ID>=200218 AND D.FINANCIAL_WEEK_ID <=200222) ORDER BY 1;

Вам также может понравиться