Oracle
Partitioning, first introduced in Oracle 8.0 in 1997, is one of
the most important and successful functionalities of the Oracle database,
improving the performance, manageability, and availability for tens of
thousands of applications. Partitioning
is a divide-and-conquer approach to improving Oracle maintenance and SQL
performance. Anyone with un-partitioned databases over 500 gigabytes is
courting disaster. Databases become unmanageable, and serious problems
occur:
- Files recovery takes days, not minutes
- Rebuilding indexes (important to re-claim space and
improve performance) can take days
- Queries with full-table scans take hours to complete
- Index range scans become inefficient
Partitioning
·
enhances the
performance, manageability, and availability of a wide variety of applications
·
Helps reduce the
total cost of ownership for storing large amounts of data.
·
Allows tables,
indexes, and index-organized tables to be subdivided into smaller pieces,
enabling these database objects to be managed and accessed at a finer level of
granularity.
Oracle Database 11g introduces the 8thgeneration
of partitioning which continues to offer ground-breaking new and enhanced
functionality and new partitioning techniques enabling customers to model even
more business scenarios.
Partitioning Extensions:
In addition to the basic partitioning strategies,
Oracle provides partitioning extensions. The extensions in Oracle Database 11g
mainly focus on two objectives:
(a) Enhance the manageability of a partitioned table
significantly.
(b) Extend the flexibility in defining a
partitioning key.
Describing here the Oracle 11g Features found very
useful during development phase of the Project:
1.
Interval Partitioning
2.
Exchange Partition
3.
Compress Partition
.
1.
Interval Partitioning
Interval
Partitioning: A new partitioning strategy in Oracle Database 11g,
Interval Partitioning extends the capabilities of the range method to define
equipartitioned ranges using an interval definition. Rather than specifying
individual ranges explicitly, Oracle will create any partition automatically
as-needed whenever data for a partition is inserted for the very first time.
Interval Partition greatly improves the manageability of a partitioned table.
Range partitioning
allows table to be partitioned by a specified range on the partitioning
key. For example, if a table was used to store sales data, it might be
range partitioned by a DATE column, with data for each month in a different
partition.
Therefore every month a
new partition would need to be defined in order to store rows for that
month. If a row was inserted for a new month before a partition was
defined for that month, the following error would result and data loading will
fail until the new partitions are created:
ORA-14400: inserted
partition key does not map to any partition
Ø Interval Partitioning
Commands
There are a few new
commands to manage interval partitioning. First, convert a range
partitioned table to use interval partitioning by using
alter
table <table_name> set interval (expr).
Consider this range
partitioned table:
Create
table
pos_data_range (
start_date DATE,
store_id NUMBER,
inventory_id NUMBER (6),
qty_sold NUMBER (3)
)
PARTITION BY RANGE (start_date)
(
PARTITION pos_data_p0 VALUES LESS THAN (TO_DATE ('1-7-2007', 'DD-MM-YYYY')),
PARTITION pos_data_p1 VALUES LESS THAN (TO_DATE ('1-8-2007', 'DD-MM-YYYY'))
);
pos_data_range (
start_date DATE,
store_id NUMBER,
inventory_id NUMBER (6),
qty_sold NUMBER (3)
)
PARTITION BY RANGE (start_date)
(
PARTITION pos_data_p0 VALUES LESS THAN (TO_DATE ('1-7-2007', 'DD-MM-YYYY')),
PARTITION pos_data_p1 VALUES LESS THAN (TO_DATE ('1-8-2007', 'DD-MM-YYYY'))
);
If a row with a date of
August 31, 2007 is inserted into the table, it will cause an error.
insert
into pos_data_range (start_date, store_id, inventory_id, qty_sold)
values ( '31-AUG-07', 1, 1, 1);
insert into pos_data_range (start_date, store_id, inventory_id, qty_sold)
*
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition
values ( '31-AUG-07', 1, 1, 1);
insert into pos_data_range (start_date, store_id, inventory_id, qty_sold)
*
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition
·
Range Partitioned to Interval Partitioned
Alter
table pos_data_range set INTERVAL (NUMTOYMINTERVAL (1, 'MONTH'));
Interval partitioning is
now enabled, and the row with 31-AUG-07 can be inserted without error since
Oracle will automatically create the new partition.
·
Interval Partitioned to Range Partitioned
To convert the table
back to only range partitioning, use the following command:
Alter
table pos_data_range set INTERVAL();
The table is converted
back to a range partitioned table and the boundaries for the interval
partitions are set to the boundaries for the range partitions.
Ø An Interval Partitioning
Example
Interval partitioning
can simplify the manageability by automatically creating the new
partitions as needed by the data. Interval partitioning is enabled in the
table’s definition by defining one or more range partitions and including a
specified interval. For example, consider the following table:
Create
table
pos_data (
start_date DATE,
store_id NUMBER,
inventory_id NUMBER (6),
qty_sold NUMBER (3),
)
PARTITION BY RANGE (start_date)
INTERVAL (NUMTOYMINTERVAL (1, 'MONTH'))
(
PARTITION pos_data_p2 VALUES LESS THAN (TO_DATE ('1-7-2007', 'DD-MM-YYYY')),
PARTITION pos_data_p3 VALUES LESS THAN (TO_DATE ('1-8-2007', 'DD-MM-YYYY'))
);
pos_data (
start_date DATE,
store_id NUMBER,
inventory_id NUMBER (6),
qty_sold NUMBER (3),
)
PARTITION BY RANGE (start_date)
INTERVAL (NUMTOYMINTERVAL (1, 'MONTH'))
(
PARTITION pos_data_p2 VALUES LESS THAN (TO_DATE ('1-7-2007', 'DD-MM-YYYY')),
PARTITION pos_data_p3 VALUES LESS THAN (TO_DATE ('1-8-2007', 'DD-MM-YYYY'))
);
Here, two partitions
have been defined and an interval of one month has been specified. If
data is loaded into this table with a later date than the greatest defined
partition, Oracle will automatically create a new partition for the new
month. In the table above, the greatest defined interval is between July
1, 2007 and August 1, 2007.
Inserting a row that has
a date later than or equal to August 1, 2007 would raise an error with Normal range partitioning unless you
create a partition for the new period and insert rows for that period into the
table. However,
with interval partitioning, Oracle determines the high value of the defined
range partitions, called the transition point, and creates new partitions for
data that is beyond that high value.
Insert
into pos_data (start_date, store_id, inventory_id, qty_sold) values
('31-AUG-07, 1, 1, 1);
SELECT TABLE_NAME, PARTITION_NAME, PARTITION_POSITION, HIGH_VALUE
FROM DBA_TAB_PARTITIONS
WHERE TABLE_NAME='POS_DATA'
WHERE TABLE_NAME='POS_DATA'
|
PARTITION_NAME
|
HIGH_VALUE
|
|
POS_DATA_P0
|
TO_DATE(' 2007-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS',
'NLS_CALENDAR=GREGORIAN')
|
|
POS_DATA_P1
|
TO_DATE(' 2007-08-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS',
'NLS_CALENDAR=GREGORIAN')
|
|
SYS_P81
|
TO_DATE(' 2007-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS',
'NLS_CALENDAR=GREGORIAN')
|
Notice that a system
generated partition named SYS_P81 has been created upon inserting a row with a
partition key greater than the transition point. Oracle will manage the
creation of new partitions for any Partition Key value. Therefore, the
values do not need to be inserted in sequence.
Since the partitions are
named automatically, Oracle has added a new syntax in order to reference
specific partitions effectively. The normal way to reference a specific
partition is to use the partition (partition_name) in the
query:
Select * from pos_data
partition (SYS_P81);
However, it would be
cumbersome to look up the system generated partition name each time. Therefore,
the new syntax to specify a partition is by using the partition for
(DATE) clause in the query:
Select * from pos_data
partition for (to_date ('31-AUG-2007','dd-mon-yyyy'));
Ø Restrictions
on Interval Partitioning
- Cannot be used for index organized tables
- Must use only one partitioning key column and it must
be a DATE or NUMBER
- Cannot create domain indexes on interval partitioned
tables
- Are not supported at the sub-partition level
·
This feature should be
used as an enhancement to range partitioning when uniform distribution of range
intervals for new partitions is acceptable. If the requirement demands the use
of uneven intervals when adding new partitions, then interval partitioning
would not be the best solution.
2.
Exchanging Partitions and Sub partitions with Tables
One of the great features about partitioning, and most
specifically range-based partitioning, is the ability to load data quickly and
easily with minimal impact on the current users using ALTER TABLE ...
EXCHANGE PARTITION statement—that is, to move any existing rows in the
partition or sub partition to the no partitioned table, and any existing rows
in the no partitioned table to the table partition or sub partition.
ALTER
TABLE pt
EXCHANGE
PARTITION p
WITH TABLE
t
INCLUDING
INDEXES
WITHOUT VALIDATION;
WITHOUT VALIDATION;
Where pt is the partitioned table, p is
the partition or subpartition to be exchanged, and t is the nonpartitioned
table to be exchanged with p.
This command swaps over the definitions
of the named partition and the table t, so that the data suddenly exists in the
right place in the partitioned table. Moreover, with the inclusion of the two
optional extra clauses, index definitions will be swapped and Oracle will not
check whether the data actually belongs in the partition - so the exchange is
very quick.
Ø An Exchange Partition
Example
Alter
table call exchange partition data_2007 with table call_temp;
Below
is an example where it is observed that while moving 180 MB of data:
(1)
Create as select method it takes around 15 seconds
(2) Partition exchange method is taking only three seconds.
(2) Partition exchange method is taking only three seconds.
The
partition exchange method also takes care of the index on the source table. So
efficiency of the partition exchange is more than five times.
Test
Instance
Schema: XYZ_A01 (with DBA privilege)
Source Table : CALL_TEMP
Destination Table : CALL (with partition)
Schema: XYZ_A01 (with DBA privilege)
Source Table : CALL_TEMP
Destination Table : CALL (with partition)
--Start with a clean schema
DROP TABLE call;
DROP TABLE call_temp;
DROP TABLE call_temp;
Select owner||’.’||segment_name OBJECT_NAME, round (bytes/1024/1024,1)
MB from dba_segments
where owner = ‘XYZ_A01’ and
segment_name like ‘%CALL%’;
--no
rows selected
Start
time keeping
Set timing on
--Create the temporary (source) Table which is used to load the
data offline
DROP TABLE call_temp;
CREATE TABLE call_temp (
id NUMBER(12,6),
v1 VARCHAR2(10),
data VARCHAR2(100)
);
CREATE TABLE call_temp (
id NUMBER(12,6),
v1 VARCHAR2(10),
data VARCHAR2(100)
);
--Insert Records into the offline (source) table
Load
1'000'000 Rows into the offline (source) table
INSERT /*+ append ordered full(s1) use_nl(s2) */
INTO call_temp
SELECT
TRUNC((ROWNUM-1)/500,6),
TO_CHAR(ROWNUM),
RPAD('X',100,'X')
FROM
all_tables s1,
all_tables s2
WHERE
ROWNUM <= 1000000;
commit;
INSERT /*+ append ordered full(s1) use_nl(s2) */
INTO call_temp
SELECT
TRUNC((ROWNUM-1)/500,6),
TO_CHAR(ROWNUM),
RPAD('X',100,'X')
FROM
all_tables s1,
all_tables s2
WHERE
ROWNUM <= 1000000;
commit;
Elapsed:
00:05:44.09
--Check size of the Temp table
select owner||’.’||segment_name OBJECT_NAME, round (bytes/1024/1024,1)
MB from dba_segments
where owner = ‘XYZ_A01’ and
segment_name like ‘%CALL%’;
--------------------------------\
OBJECT_NAME MB
-------------------- ----------
XYZ_A01.CALL_TEMP 189.8
--------------------------------/
OBJECT_NAME MB
-------------------- ----------
XYZ_A01.CALL_TEMP 189.8
--------------------------------/
--Add Primary Key to the offline Table
--ALTER TABLE call_temp
--ADD CONSTRAINT pk_call_temp PRIMARY KEY (id)
--USING INDEX (CREATE INDEX pk_call_temp ON call_temp (id) NOLOGGING);
CREATE INDEX
pk_call_temp ON call_temp (id) NOLOGGING;
--(Elapsed: 00:00:06.09)
--(Elapsed: 00:00:06.09)
ALTER TABLE call_temp
ADD CONSTRAINT pk_call_temp PRIMARY KEY (id);
--(Elapsed: 00:00:07.04)
----Check size of the Temp table and the Index
Select owner||’.’||segment_name OBJECT_NAME, round (bytes/1024/1024,1)
MB from dba_segments where owner = ‘XYZ_A01’ and segment_name like ‘%CALL%’;
---------------------------------------\
OBJECT_NAME MB
-------------------- ----------
XYZ_A01.CALL_TEMP 189.8
XYZ_A01.PK_CALL_TEMP 25
-------------------------------/
OBJECT_NAME MB
-------------------- ----------
XYZ_A01.CALL_TEMP 189.8
XYZ_A01.PK_CALL_TEMP 25
-------------------------------/
---Case I: Create destination table directly
drop table call;
create table call as select * from call_temp;
drop table call;
create table call as select * from call_temp;
Elapsed: 00:00:14.03 --I
---Case II: Create the
partition (destination) table
DROP TABLE call;
CREATE TABLE call (
id NUMBER(12,6),
v1 VARCHAR2(10),
data VARCHAR2(100)
)
PARTITION BY RANGE(id) ( -- Partion Key = Primary Key
PARTITION call_partition VALUES LESS THAN (MAXVALUE)
);
DROP TABLE call;
CREATE TABLE call (
id NUMBER(12,6),
v1 VARCHAR2(10),
data VARCHAR2(100)
)
PARTITION BY RANGE(id) ( -- Partion Key = Primary Key
PARTITION call_partition VALUES LESS THAN (MAXVALUE)
);
Select owner||’.’||segment_name OBJECT_NAME, round
(bytes/1024/1024, 1) MB from dba_segments where owner = ‘XYZ_A01’ and
segment_name like ‘%CALL%’;
------------------------------------------\
OBJECT_NAME MB
-------------------- ----------
XYZ_A01.CALL_TEMP 189.8
XYZ_A01.CALL 0
XYZ_A01.PK_CALL_TEMP 25
------------------------------------------/
OBJECT_NAME MB
-------------------- ----------
XYZ_A01.CALL_TEMP 189.8
XYZ_A01.CALL 0
XYZ_A01.PK_CALL_TEMP 25
------------------------------------------/
--Add local primary key to the partition table as a local index
(destination)
--ALTER TABLE call
--ADD CONSTRAINT pk_call PRIMARY KEY(id)
--USING INDEX (CREATE INDEX pk_call ON CALL(id) NOLOGGING LOCAL);
CREATE INDEX pk_call ON CALL(id) NOLOGGING LOCAL;
ALTER TABLE call
ADD CONSTRAINT pk_call PRIMARY KEY(id);
--------------------------------\
OBJECT_NAME MB
-------------------- ----------
XYZ_A01.CALL_TEMP 189.8
XYZ_A01.CALL 0
XYZ_A01.PK_CALL_TEMP 25
XYZ_A01.PK_CALL 0
--------------------------------/
OBJECT_NAME MB
-------------------- ----------
XYZ_A01.CALL_TEMP 189.8
XYZ_A01.CALL 0
XYZ_A01.PK_CALL_TEMP 25
XYZ_A01.PK_CALL 0
--------------------------------/
--Now swap the offline Table into the partition (source into
destination)
=====================================================================
ALTER TABLE CALL
EXCHANGE PARTITION call_partition WITH TABLE call_temp
INCLUDING INDEXES
WITHOUT VALIDATION;
=====================================================================
ALTER TABLE CALL
EXCHANGE PARTITION call_partition WITH TABLE call_temp
INCLUDING INDEXES
WITHOUT VALIDATION;
Elapsed: 00:00:02.06 –II
--Check size of the destination table after Exchange
select owner||’.’||segment_name OBJECT_NAME, round (bytes/1024/1024,1)
MB from dba_segments where owner = ‘XYZ_A01’ and segment_name like ‘%CALL%’;
----------------------------------------\
OBJECT_NAME MB
-------------------- ----------
XYZ_A01’.CALL_TEMP 0
XYZ_A01’.CALL 189.8
XYZ_A01’.PK_CALL_TEMP 0
XYZ_A01’.PK_CALL 25
----------------------------------------/
OBJECT_NAME MB
-------------------- ----------
XYZ_A01’.CALL_TEMP 0
XYZ_A01’.CALL 189.8
XYZ_A01’.PK_CALL_TEMP 0
XYZ_A01’.PK_CALL 25
----------------------------------------/
So
data from main table as well as index moved to the destination.
Conclusion:
The exchange partition method is 14.03/02.06
= 6.8 times faster than normal
insert method.
Ø Restrictions
The table to be exchanged must not be
partitioned, but must otherwise have the same table structure as the
partitioned table.
·
The table to be
exchanged must not be a temporary table.
·
Any rows existing in the
nonpartitioned table prior to the exchange must lie within the range defined
for the partition or subpartition.
·
The table to be
exchanged may not have any foreign keys, nor may any other tables have foreign
keys which reference this table. The table to be exchanged may not have any
foreign keys, nor may any other tables have foreign keys which reference this
table.
·
In addition to the
ALTER, INSERT, and CREATE privileges usually required for ALTER TABLE statements, you must have the DROP privilege to perform ALTER TABLE ... EXCHANGE
PARTITION.
·
Executing this statement
does not invoke any triggers on either the partitioned table or the exchanged
table.
·
Any AUTO_INCREMENT
columns in the exchanged table are reset.
3.
Partition compression
As we know that in 10g we were able to compress on whole table only
but in 11g we can use compress on partition level.
Ø Syntax:
ALTER
TABLE... COMPRESS;
ALTER
TABLE... NOCOMPRESS;
Ø
Compressing Table at Partition Level.
Example:
CREATE TABLE test
(sno NUMBER(6),
last_name VARCHAR2 (30),
salary NUMBER (6))
PARTITION BY RANGE (salary)
INTERVAL (5000)
(
PARTITION
p1 VALUES LESS THAN (5000) COMPRESS,
PARTITION
p2 VALUES LESS THAN (10000) NOCOMPRESS,
PARTITION
p3 VALUES LESS THAN (15000) COMPRESS,
PARTITION
p4 VALUES LESS THAN (20000)) NOCOMPRESS;
SELECT table_name, partition_name, compression
FROM
user_tab_partitions
WHERE
table_name='TEST';
TABLE_NAME
PARTITION_NAME
COMPRESS
--------------- ---------------------------
--------------
TEST
P1 ENABLED
TEST
P2
DISABLED
TEST
P3
ENABLED
TEST
P4
DISABLED
·
We
can define compression for whole partition by using single keyword ‘COMPRESS’ like this.
CREATE TABLE test
(sno NUMBER (6),
last_name VARCHAR2 (30),
salary NUMBER (6))
COMPRESS
PARTITION BY RANGE (salary)
INTERVAL (5000)
(
PARTITION p1 VALUES LESS THAN (5000),
PARTITION p2 VALUES LESS THAN (10000),
PARTITION p3 VALUES LESS THAN (15000),
PARTITION p4 VALUES LESS THAN (20000));
Table created.
SELECT table_name, partition_name, compression
FROM
user_tab_partitions
WHERE
table_name='TEST';
TABLE_NAME PARTITION_NAME COMPRESS
------------------------------
----------------------------- -------------
TEST P1 ENABLED
TEST P2 ENABLED
TEST P3 ENABLED
TEST P4 ENABLED
Ø Compressing Individual
Partitions in the warehouse
Let us consider the following example to
illustrate how the Compress Partitioning feature saves a large amount of costly
space on disk.
-- Check size of Partition before compress
select segment_name, sum(bytes)/(1024*1024*1024) table_size_meg
from user_extents where segment_type='TABLE
PARTITION'
and
segment_name = ‘VAL_TEMP’
AND PARTITION_NAME ='VAL_TEMP_VCHR_201309'
group by segment_name;
segment_name table_size_meg
---------------- --------------
VAL_TEMP 14.3974609375
---------------- --------------
VAL_TEMP 14.3974609375
--Compress Table for
Partition VAL_TEMP_VCHR_201309
group by segment_name;
ALTER TABLE VAL_TEMP
MOVE partition VAL_TEMP_VCHR_201309
COMPRESS FOR ALL OPERATIONS
NOLOGGING;
--Check size of Partition after compress
select segment_name,sum(bytes)/(1024*1024*1024)
table_size_meg
from user_extents where segment_type='TABLE
PARTITION' and
segment_name = 'VAL_TEMP’
AND PARTITION_NAME ='VAL_TEMP_VCHR_201309'
group by segment_name;
segment_name table_size_meg
---------------- --------------
VAL_TEMP 1.1552734375
---------------- --------------
VAL_TEMP 1.1552734375
That
gives us a compression ratio of 12.5 which is quite nice if our goal is
to reduce costly space on disk.
Ø COMPRESS FOR ALL OPERATIONS
select
compression, compress_for
from user_tab_partitions
where partition_name = 'SALES_1995';
from user_tab_partitions
where partition_name = 'SALES_1995';
You
get something like this:
COMPRESS COMPRESS_FOR
----------- --------------------
ENABLED DIRECT LOAD ONLY
----------- --------------------
ENABLED DIRECT LOAD ONLY
Compress
table at Partition level
ALTER TABLE
sales
MOVE PARTITION sales_1995
COMPRESS FOR ALL OPERATIONS
NOLOGGING;
MOVE PARTITION sales_1995
COMPRESS FOR ALL OPERATIONS
NOLOGGING;
And
ask again, what we compressed for you get the expected result:
COMPRESS COMPRESS_FOR
-------------- ------------------------
ENABLED FOR ALL OPERATIONS
-------------- ------------------------
ENABLED FOR ALL OPERATIONS
What
this means is that we now have compression on, and if we insert a row, or
update a row, we still keep the compression going. The earlier status is really
the one to use for older data that you do not see changing at all. COMPRESS FOR ALL OPERATIONS is actually
the 11g Advanced Compression database priced option.
No comments:
Post a Comment