Over the past 15 years, hard disk capacities have evolved from around 10 MB to over 100 GB, and capacities are still growing. Disk arrays are fast approaching the 100 terabyte range. No matter how much storage is available, however, there is always a way to exhaust it. As databases grow in size, day-to-day operations become more and more challenging. For example, finding the time and resources to rebuild an index containing 100 million entries can prove quite demanding. Prior to Oracle8, database administrators would meet this challenge by manually breaking a large table into several smaller tables. Although the pieces could be hidden behind a special type of view (called a partition view) during a query, all DML statements had to be performed against the individual tables, thereby exposing the partitioning scheme to the database developers and users.
Starting with Version 8.0, Oracle provided a means for breaking a table into multiple pieces while preserving the look and feel of a single table. Each piece is called a partition, and, although every partition must share the same columns, constraints, indexes, and triggers, each partition can have its own unique storage parameters. While administrators generally deal with individual partitions when allocating storage and performing backups, developers may choose to deal with either the entire table or with individual partitions.
Database designers and administrators have been partitioning tables since long before Oracle8 hit the scene. Generally, table partitioning within a single database is done to improve performance and simplify administration tasks, while table partitioning between databases is meant to facilitate data distribution. For example, sales data might be partitioned by region and each partition hosted in a database housed at its respective regional sales office. Whereas a central data warehouse might gather sales data from each office for reporting and decision-support queries, it might be perfectly reasonable for the operational sales data to be distributed across multiple sites.
Partitioning by sets of rows such as in the sales data example, in which the value of the sales office column determines where the data resides, is known as horizontal partitioning. Partitioning may also be accomplished by splitting up sets of columns, in which case it is called vertical partitioning. For example, sensitive data such as salary information and social security numbers may be split off from the employee table into a separate table with restricted access. When partitioning vertically, primary key columns must be included in the set of columns for every partition. Therefore, unlike horizontal partitioning, where each partition contains nonoverlapping subsets of data, vertical partitioning mandates that some data be duplicated in each partition.
While both vertical and horizontal partitioning may be accomplished manually within and between Oracle databases, the Partitioning Option introduced in Oracle8 specifically deals with horizontal partitioning within a single database.
When partitioning is employed, a table changes from a physical object to a virtual concept. There isn’t really a table anymore, just a set of partitions. Since all of the partitions must share the same attribute and constraint definitions, however, it is possible to deal with the set of partitions as if they were a single table. The storage parameters, such as extent sizes and tablespace placement, are the only attributes that may differ among the partitions. This situation can facilitate some interesting storage scenarios, such as hosting infrequently accessed partitions on a CD jukebox while the heavily-hit data partitions reside on disk. You can also take advantage of Oracle’s segmented buffer cache to keep the most active partitions in the keep buffer so they are always in memory, while the rest of the partitions can be targeted for the recycle or default buffers. Additionally, individual partitions may be taken offline without affecting the availability of the rest of the partitions, giving administrators a great deal of flexibility.
Depending on the partitioning scheme employed, you must choose one or more columns of a table to be the partition key. The values of the columns in the partition key determine the partition that hosts a particular row. Oracle also uses the partition key information in concert with your WHERE clauses to determine which partitions to search during SELECT, UPDATE, and DELETE operations (see Section 10.6 later in the chapter for more information).
So what, you may wonder, happens to the indexes on partitioned tables? The answer is that you have to choose whether each index will stay intact (referred to as a global index), or be split into pieces corresponding to the table partitions (referred to as a local index). Furthermore, with a global index, you can choose to partition the index in a different manner than the table was partitioned. When you throw the fact that you can partition both b-tree and bit-map indexes into the mix, things can become overwhelming. When you issue a SELECT, UPDATE, or DELETE statement against a partitioned table, the optimizer can take several routes to locate the target rows:
Use a global index, if one is available and its columns are referenced in the SQL statement, to find the target rows across one or more partitions.
Search a local index on every partition to identify whether any particular partition contains target rows.
Define a subset of the partitions that might contain target rows, and then access local indexes on those partitions.
Although global indexes might seem to be the simplest solution, they can be problematic. Because global indexes span all of the partitions of a table, they are adversely affected by partition maintenance operations. For example, if a partition is split into multiple pieces, or if two partitions are merged into one, all global indexes on the partitioned table are marked as UNUSABLE and must be rebuilt before they can be used again. When modifying a partitioning scheme, you have your choice of rebuilding the global indexes manually, or of using the UPDATE GLOBAL INDEXES clause.
To horizontally partition a table (or index), you must specify a set of rules so that Oracle can determine in which partition a given row should reside. The following sections explore the five types of partitioning available in Oracle Database 10g.
The first partitioning scheme, introduced in Oracle8 and known as range partitioning, allows a table to be partitioned over ranges of values for one or more columns of the table. The simplest and most widely implemented form of range partitioning is to partition using a single date column. Consider the following DDL statement:
CREATE TABLE cust_order ( order_nbr NUMBER(7) NOT NULL, cust_nbr NUMBER(5) NOT NULL, order_dt DATE NOT NULL, sales_emp_id NUMBER(5) NOT NULL, sale_price NUMBER(9,2), expected_ship_dt DATE, cancelled_dt DATE, ship_dt DATE, status VARCHAR2(20) ) PARTITION BY RANGE (order_dt) (PARTITION orders_2000 VALUES LESS THAN (TO_DATE('01-JAN-2001','DD-MON-YYYY')) TABLESPACE ord1, PARTITION orders_2001 VALUES LESS THAN (TO_DATE('01-JAN-2002','DD-MON-YYYY')) TABLESPACE ord2, PARTITION orders_2002 VALUES LESS THAN (TO_DATE('01-JAN-2003','DD-MON-YYYY')) TABLESPACE ord3);
Using this partitioning scheme, all orders prior to 2001 will reside
in the orders_2000
p
artition;
orders from 2001 will reside in the orders_2001
partition; and orders for the year 2002 will reside in the
orders_2002
partition.
In some
cases, you
may wish to partition a large table, but there are no columns for
which range partitioning is suitable. Available in
Oracle8i, hash partitioning
allows you to specify the number of partitions and the partition
columns (the partition key), but leaves the allocation of rows to
partitions up to Oracle. As rows are inserted into the partitioned
table, Oracle attempts to evenly spread the data across the
partitions by applying a hashing function to the data in the
partition key; the value returned
by the hashing function determines the partition that hosts the row.
If the partition columns are included in the WHERE clause of a
SELECT, DELETE, or UPDATE statement, Oracle can apply the hash
function to determine which partition to search. The following DDL
statement demonstrates how the part
table might be
partitioned by hashing the part_nbr
column:
CREATE TABLE part ( part_nbr VARCHAR2(20) NOT NULL, name VARCHAR2(50) NOT NULL, supplier_id NUMBER(6) NOT NULL, inventory_qty NUMBER(6) NOT NULL, status VARCHAR2(10) NOT NULL, unit_cost NUMBER(8,2), resupply_date DATE ) PARTITION BY HASH (part_nbr) (PARTITION part1 TABLESPACE p1, PARTITION part2 TABLESPACE p2, PARTITION part3 TABLESPACE p3, PARTITION part4 TABLESPACE p4);
For the data to be evenly distributed across the partitions, it is important to choose columns with high cardinality as partition keys. A set of columns is said to have high cardinality if the number of distinct values is large compared to the size of the table. Choosing a high cardinality column for your partition key ensures an even distribution across your partitions; otherwise, the partitions can become unbalanced, causing performance to be unpredictable and making administration more difficult.
If you are torn between whether to apply range or hash partitioning to your table, you can do some of each. Composite partitioning, also unveiled with Oracle8i, allows you to create multiple range partitions, each of which contains two or more hash subpartitions. There are two types of composite partitioning, range-hash partitioning , which was unveiled in Oracle8i, and range-list partitioning , which we’ll talk about later in this chapter.
Composite partitioning is often useful when range partitioning is
appropriate for the type of data stored in the table, but you want a
finer granularity of partitioning than is practical using range
partitioning alone. For example, it might make sense to partition
your order
table by year based on the types of
queries against the table. If a year’s worth of data
proves too cumbersome for a single partition, however, you could
subpartition each year by hashing the customer number across four
buckets. The following example expands on the range-partitioning
example shown earlier by generating subpartitions based on a hash of
the customer number:
CREATE TABLE cust_order ( order_nbr NUMBER(7) NOT NULL, cust_nbr NUMBER(5) NOT NULL, order_dt DATE NOT NULL, sales_emp_id NUMBER(5) NOT NULL, sale_price NUMBER(9,2), expected_ship_dt DATE, cancelled_dt DATE, ship_dt DATE, status VARCHAR2(20) ) PARTITION BY RANGE (order_dt) SUBPARTITION BY HASH (cust_nbr) SUBPARTITIONS 4 STORE IN (order_sub1, order_sub2, order_sub3, order_sub4) (PARTITION orders_2000 VALUES LESS THAN (TO_DATE('01-JAN-2000','DD-MON-YYYY')) (SUBPARTITION orders_2000_s1 TABLESPACE order_sub1, SUBPARTITION orders_2000_s2 TABLESPACE order_sub2, SUBPARTITION orders_2000_s3 TABLESPACE order_sub3, SUBPARTITION orders_2000_s4 TABLESPACE order_sub4), PARTITION orders_2001 VALUES LESS THAN (TO_DATE('01-JAN-2001','DD-MON-YYYY')) (SUBPARTITION orders_2001_s1 TABLESPACE order_sub1, SUBPARTITION orders_2001_s2 TABLESPACE order_sub2, SUBPARTITION orders_2001_s3 TABLESPACE order_sub3, SUBPARTITION orders_2001_s4 TABLESPACE order_sub4), PARTITION orders_2002 VALUES LESS THAN (TO_DATE('01-JAN-2002','DD-MON-YYYY')) (SUBPARTITION orders_2002_s1 TABLESPACE order_sub1, SUBPARTITION orders_2002_s2 TABLESPACE order_sub2, SUBPARTITION orders_2002_s3 TABLESPACE order_sub3, SUBPARTITION orders_2002_s4 TABLESPACE order_sub4));
Interestingly, when composite partitioning is used, all of the data is physically stored in the subpartitions, while the partitions, just like the table, become virtual.
Introduced in Oracle9i, list partitioning allows a table to be partitioned by one or more distinct values of a particular column. For example, a warehouse table containing sales summary data by product, state, and month/year could be partitioned into geographic regions, as in:
CREATE TABLE sales_fact ( state_cd VARCHAR2(3) NOT NULL, month_cd NUMBER(2) NOT NULL, year_cd NUMBER(4) NOT NULL, product_cd VARCHAR2(10) NOT NULL, tot_sales NUMBER(9,2) NOT NULL ) PARTITION BY LIST (state_cd) (PARTITION sales_newengland VALUES ('CT','RI','MA','NH','ME','VT') TABLESPACE s1, PARTITION sales_northwest VALUES ('OR','WA','MT','ID','WY','AK') TABLESPACE s2, PARTITION sales_southwest VALUES ('NV','UT','AZ','NM','CO','HI') TABLESPACE s3, PARTITION sales_southeast VALUES ('FL','GA','AL','SC','NC','TN','WV') TABLESPACE s4, PARTITION sales_east VALUES ('PA','NY','NJ','MD','DE','VA','KY','OH') TABLESPACE s5, PARTITION sales_california VALUES ('CA') TABLESPACE s6, PARTITION sales_south VALUES ('TX','OK','LA','AR','MS') TABLESPACE s7, PARTITION sales_midwest VALUES ('ND','SD','NE','KS','MN','WI','IA', 'IL','IN','MI','MO') TABLESPACE s8);
List partitioning is appropriate for low cardinality data in which the number of distinct values of a column is small relative to the number of rows. Unlike range and hash partitioning, where the partition key may contain several columns, list partitioning is limited to a single column.
Available in the Oracle Database 10g release, range-list composite partitioning allows you to partition your data by range, and then subpartition via a list. This might be an excellent strategy for partitioning data in a sales warehouse so that you could partition your data both on sales periods (i.e., years, quarters, months) and on sales regions (i.e., states, countries, districts). The following example expands on the list partitioning example by adding yearly partitions:
CREATE TABLE sales_fact ( state_cd VARCHAR2(3) NOT NULL, month_cd NUMBER(2) NOT NULL, year_cd NUMBER(4) NOT NULL, product_cd VARCHAR2(10) NOT NULL, tot_sales NUMBER(9,2) NOT NULL ) PARTITION BY RANGE (year_cd) SUBPARTITION BY LIST (state_cd) (PARTITION sales_2000 VALUES LESS THAN (2001) (SUBPARTITION sales_2000_newengland VALUES ('CT','RI','MA','NH','ME','VT') TABLESPACE s1, SUBPARTITION sales_2000_northwest VALUES ('OR','WA','MT','ID','WY','AK') TABLESPACE s2, SUBPARTITION sales_2000_southwest VALUES ('NV','UT','AZ','NM','CO','HI') TABLESPACE s3, SUBPARTITION sales_2000_southeast VALUES ('FL','GA','AL','SC','NC','TN','WV') TABLESPACE s4, SUBPARTITION sales_2000_east VALUES ('PA','NY','NJ','MD','DE','VA','KY','OH') TABLESPACE s5, SUBPARTITION sales_2000_california VALUES ('CA') TABLESPACE s6, SUBPARTITION sales_2000_south VALUES ('TX','OK','LA','AR','MS') TABLESPACE s7, SUBPARTITION sales_2000_midwest VALUES ('ND','SD','NE','KS','MN','WI','IA', 'IL','IN','MI','MO') TABLESPACE s8 ), PARTITION sales_2001 VALUES LESS THAN (2002) (SUBPARTITION sales_2001_newengland VALUES ('CT','RI','MA','NH','ME','VT') TABLESPACE s1, SUBPARTITION sales_2001_northwest VALUES ('OR','WA','MT','ID','WY','AK') TABLESPACE s2, SUBPARTITION sales_2001_southwest VALUES ('NV','UT','AZ','NM','CO','HI') TABLESPACE s3, SUBPARTITION sales_2001_southeast VALUES ('FL','GA','AL','SC','NC','TN','WV') TABLESPACE s4, SUBPARTITION sales_2001_east VALUES ('PA','NY','NJ','MD','DE','VA','KY','OH') TABLESPACE s5, SUBPARTITION sales_2001_california VALUES ('CA') TABLESPACE s6, SUBPARTITION sales_2001_south VALUES ('TX','OK','LA','AR','MS') TABLESPACE s7, SUBPARTITION sales_2001_midwest VALUES ('ND','SD','NE','KS','MN','WI','IA', 'IL','IN','MI','MO') TABLESPACE s8 ), PARTITION sales_2002 VALUES LESS THAN (2003) (SUBPARTITION sales_2002_newengland VALUES ('CT','RI','MA','NH','ME','VT') TABLESPACE s1, SUBPARTITION sales_2002_northwest VALUES ('OR','WA','MT','ID','WY','AK') TABLESPACE s2, SUBPARTITION sales_2002_southwest VALUES ('NV','UT','AZ','NM','CO','HI') TABLESPACE s3, SUBPARTITION sales_2002_southeast VALUES ('FL','GA','AL','SC','NC','TN','WV') TABLESPACE s4, SUBPARTITION sales_2002_east VALUES ('PA','NY','NJ','MD','DE','VA','KY','OH') TABLESPACE s5, SUBPARTITION sales_2002_california VALUES ('CA') TABLESPACE s6, SUBPARTITION sales_2002_south VALUES ('TX','OK','LA','AR','MS') TABLESPACE s7, SUBPARTITION sales_2002_midwest VALUES ('ND','SD','NE','KS','MN','WI','IA', 'IL','IN','MI','MO') TABLESPACE s8 ));
Rather than specifying the same list information over and over, Oracle Database 10g now allows the use of subpartition templates so that the subpartitioning scheme can be specified just once:
CREATE TABLE sales_fact (
state_cd VARCHAR2(3) NOT NULL,
month_cd NUMBER(2) NOT NULL,
year_cd NUMBER(4) NOT NULL,
product_cd VARCHAR2(10) NOT NULL,
tot_sales NUMBER(9,2) NOT NULL
)
PARTITION BY RANGE (year_cd)
SUBPARTITION BY LIST (state_cd)
SUBPARTITION TEMPLATE
(
SUBPARTITION newengland
VALUES ('CT','RI','MA','NH','ME','VT') TABLESPACE s1,
SUBPARTITION northwest
VALUES ('OR','WA','MT','ID','WY','AK') TABLESPACE s2,
SUBPARTITION southwest
VALUES ('NV','UT','AZ','NM','CO','HI') TABLESPACE s3,
SUBPARTITION southeast
VALUES ('FL','GA','AL','SC','NC','TN','WV') TABLESPACE s4,
SUBPARTITION east
VALUES ('PA','NY','NJ','MD','DE','VA','KY','OH') TABLESPACE s5,
SUBPARTITION california
VALUES ('CA') TABLESPACE s6,
SUBPARTITION south
VALUES ('TX','OK','LA','AR','MS') TABLESPACE s7,
SUBPARTITION midwest
VALUES ('ND','SD','NE','KS','MN','WI','IA', 'IL','IN','MI','MO')
TABLESPACE s8
)
(PARTITION sales_2000
VALUES LESS THAN (2001),
PARTITION sales_2001
VALUES LESS THAN (2002),
PARTITION sales_2002
VALUES LESS THAN (2003)
);
When you are writing SQL against partitioned tables, you have the option to treat the partitions as single, virtual tables, or to specify partition names within your SQL statements. If you write DML against a virtual table, the Oracle optimizer determines the partition or partitions that need to be involved. For an INSERT statement, the optimizer uses the values provided for the partition key to determine where to put each row. For UPDATE, DELETE, and SELECT statements, the optimizer uses the conditions from the WHERE clause along with information on local and global indexes to determine the partition or partitions that need to be searched.
If you know that your DML statement will utilize a single partition,
and you know the name of the partition, you can use the
PARTITION
clause to tell the optimizer which partition to use. For example, if
you want to summarize all orders for the year 2000, and you know that
the cust_order
table is range-partitioned by year,
you could issue the following query:
SELECT COUNT(*) tot_orders, SUM(sale_price) tot_sales FROM cust_order PARTITION (orders_2000) WHERE cancelled_dt IS NULL;
This query’s WHERE clause doesn’t
specify a date range, even though the table contains data spanning
multiple years. Because you specified the
orders_2000
partition, you know that the query
will only summarize orders from 2000, so there is no need to check
each order’s date.
If your table is composite-partitioned, you can use the
SUBPARTITION clause to focus on a single
subpartition of the table. For example, the following statement
deletes all rows from the orders_2000_s1
subpartition of the range-hash composite-partitioned version of the
cust_order
table:
DELETE FROM cust_order SUBPARTITION (orders_2000_s1);
You can also use the PARTITION clause to delete the entire set of subpartitions that fall within a given partition:
DELETE FROM cust_order PARTITION (orders_2000);
This statement would delete all rows from the
orders_2000_s1
, orders_2000_s2
,
orders_2000_s3
, and
orders_2000_s4
subpartitions of the
cust_order
table.
Here are a few additional things to consider when working with partitioned tables:
If the optimizer determines that two or more partitions are needed to satisfy the WHERE clause of a SELECT, UPDATE, or DELETE statement, the table and/or index partitions may be scanned in parallel. Therefore, depending on the system resources available to Oracle, scanning every partition of a partitioned table could be much faster than scanning an entire unpartitioned table.
Because hash partitioning spreads data randomly across the partitions,[1] we don’t believe you would ever want to use the PARTITION clause for hash-partitioned tables or the SUBPARTITON clause for range-hash partitioned tables, since you don’t know what data you are working on. The only reasonable scenario that comes to mind might be when you want to modify every row in the table, but you don’t have enough rollback available to modify every row in a single transaction. In this case, you can perform an UPDATE or DELETE on each partition or subpartition and issue a COMMIT after each statement completes.
Partitions can be merged, split, or dropped at any time by the DBA. Therefore, use caution when explicitly naming partitions in your DML statements. Otherwise, you may find your statements failing, or worse, your statements might work on the wrong set of data because partitions have been merged or split without your knowledge. You may want to check with your DBA to determine her policy concerning naming partitions in your DML statements.
If you need to access a single partition or subpartition but don’t like having partition names sprinkled throughout your code, consider creating views to hide the partition names, as in the following:
CREATE VIEW cust_order_2000 AS SELECT * FROM cust_order PARTITION (orders_2000);
You can then issue your SQL statements against such views:
SELECT order_nbr, cust_nbr, sale_price, order_dt FROM cust_order_2000 WHERE quantity > 100;
Even when you don’t name a specific partition in a SQL statement, the fact that a table is partitioned might still influence the manner in which the statement accesses the table. When a SQL statement accesses one or more partitioned tables, the Oracle optimizer attempts to use the information in the WHERE clause to eliminate some of the partitions from consideration during statement execution. This process, called partition pruning, speeds statement execution by ignoring any partitions that cannot satisfy the statement’s WHERE clause. To do so, the optimizer uses information from the table definition combined with information from the statement’s WHERE clause. For example, given the following table definition:
CREATE TABLE tab1 ( col1 NUMBER(5) NOT NULL, col2 DATE NOT NULL, col3 VARCHAR2(10) NOT NULL) PARTITION BY RANGE (col2) (PARTITION tab1_1998 VALUES LESS THAN (TO_DATE('01-JAN-1999','DD-MON-YYYY')) TABLESPACE t1, PARTITION tab1_1999 VALUES LESS THAN (TO_DATE('01-JAN-2000','DD-MON-YYYY')) TABLESPACE t1, PARTITION tab1_2000 VALUES LESS THAN (TO_DATE('01-JAN-2001','DD-MON-YYYY')) TABLESPACE t3, PARTITION tab1_2001 VALUES LESS THAN (TO_DATE('01-JAN-2002','DD-MON-YYYY')) TABLESPACE t4);
and the following query:
SELECT col1, col2, col3 FROM tab1 WHERE col2 > TO_DATE('01-OCT-2000','DD-MON-YYYY'),
the optimizer would eliminate partitions tab1_1998
and tab1_1999
from consideration, since neither
partition could contain rows with a value for col2
greater than October 1, 2000.
For the optimizer to make these types of decisions, the WHERE clause
must reference at least one column from the set of columns that
comprise the partition key. Although this might seem fairly
straightforward, not all queries against a partitioned table
naturally include the
partition key. If a unique index exists on
the col1
column of the tab1
table from the previous example, for instance, the following query
would generally offer the most efficient access:
SELECT col1, col2, col3 FROM tab1 WHERE col1 = 1578;
If the index on col1
had been defined as a local
index, however, Oracle would need to visit each
partition’s local index to find the one that holds
the value 1578. If you also have information about the partition key
(col2
in this case), you might want to consider
including it in the query so that the optimizer can eliminate
partitions, as in the following:
SELECT col1, col2, col3 FROM tab1 WHERE col1 = 1578 AND col2 > TO_DATE('01-JAN-2001','DD-MON-YYYY'),
With the additional condition, the optimizer can now eliminate the
tab1_1998
, tab1_1999
, and
tab1_2000
partitions from consideration. Oracle
will now search a single unique index on the
tab1_2001
partition instead of searching a unique
index on each of the four table partitions. Of course, you would need
to know that data pertaining to the value 1578 also had a value for
col2
greater then January 1, 2001. If you can
reliably provide additional information regarding the partition keys,
than you should do so; otherwise, you’ll just have
to let the optimizer do its best. Running EXPLAIN PLAN on your DML
statements against partitioned tables will allow you to see which
partitions the optimizer decided to utilize.
When checking the results of EXPLAIN PLAN, there are a couple of
partition specific columns that you should add to your query against
plan_table
to see which partitions are being
considered by the optimizer. To demonstrate, we’ll
explain the following query against tab1
:
EXPLAIN PLAN SET STATEMENT_ID = 'qry1' FOR SELECT col1, col2, col3 FROM tab1 WHERE col2 BETWEEN TO_DATE('01-JUL-1999','DD-MON-YYYY') AND TO_DATE('01-JUL-2000','DD-MON-YYYY'),
When querying the plan_table
table, you should
include the partition_start
and
partition_end
columns whenever the operation field
starts with 'PARTITION
':
SELECT lpad(' ',2 * level) || operation || ' ' || options || ' ' || object_name || DECODE(SUBSTR(operation, 1, 9), 'PARTITION', ' FROM ' || partition_start || ' TO ' || partition_stop, ' ') "exec plan" FROM plan_table CONNECT BY PRIOR id = parent_id START WITH id = 0 AND statement_id = 'qry1'; exec plan ------------------------------------------------------ SELECT STATEMENT PARTITION RANGE ITERATOR FROM 2 TO 3 TABLE ACCESS FULL TAB1
The value of PARTITION RANGE for the operation
column along with the value of ITERATOR for the
options
column indicates that more than one
partition will be involved in the execution plan.[2] The values of the
partition_start
and
partition_end
columns (2 and 3, respectively)
indicate that the optimizer has decided to prune partitions 1 and 4,
which correlate to the tab1_1998
and
tab1_2001
partitions.[3] Given that the WHERE clause
specifies a date range of July 1, 1999 to July 1, 2000, the optimizer
has correctly pruned all
partitions that cannot contribute
to the result
set.
[1] It isn’t actually random, but it will seem that way to you, since you don’t have access to the hash function.
[2] If
the optimizer had pruned all but one partition, the options column
would contain the value 'SINGLE
‘. If no partitions
were pruned, the options column would contain the value
'ALL
‘.
[3] The number
shown in the partition_start
and
partition_end
columns correlates to the
partition_position
column in the
user_tab_partitions
table, so you can query this
table to identify the names of the partitions that are included in
the execution plan.
18.220.237.24