Friday, October 31, 2008

Example of global partitioned, global non-partitioned and local Indexes

Index on the partitioned table can be of three types.
1)Global Non-partitioned Index.
2)Global Partitioned Index.
3)Local Partitioned Index.

With an example I will make you clear of these three different types of indexes on the partitioned table.

1)Create a partitioned table.

CREATE TABLE test_partition
(
id number,
created_date date,
col3 varchar2(20)
)
PARTITION BY RANGE (created_date)
(
PARTITION part1 VALUES LESS THAN (to_date('11-oct-2008','dd-mon-yyyy')) ,
PARTITION part2 VALUES LESS THAN (to_date('12-oct-2008','dd-mon-yyyy')) ,
PARTITION part3 VALUES LESS THAN (to_date('13-oct-2008','dd-mon-yyyy')) ,
PARTITION part4 VALUES LESS THAN (to_date('14-oct-2008','dd-mon-yyyy')) ,
PARTITION junk VALUES LESS THAN (MAXVALUE)
)
/
Table created.

Global Non Partitioned Index
This index span all over the table. Hence it is global and index is not partitioned.
SQL> create index tpid_i on test_partition(id);

Index created.

SQL> select index_name,partitioned, index_type from user_indexes where table_name='TEST_PARTITION';

INDEX_NAME PAR INDEX_TYPE
------------------------------ --- ---------------------------
TPID_I NO NORMAL

Local Partitioned Index

SQL> create index tpid_i on test_partition(id) local;

Index created.

SQL> select index_name,partitioned, index_type from user_indexes where table_name='TEST_PARTITION';

INDEX_NAME PAR INDEX_TYPE
------------------------------ --- ---------------------------
TPID_I YES NORMAL


SQL> select partition_name, status from user_ind_partitions where
2 index_name = 'TPID_I';


PARTITION_NAME STATUS
------------------------------ --------
JUNK USABLE
PART1 USABLE
PART2 USABLE
PART3 USABLE
PART4 USABLE

Global Partitioned Index

CREATE INDEX tpid_g ON test_partition(id)
GLOBAL PARTITION BY RANGE (id)
(PARTITION p1 VALUES LESS THAN (10),
PARTITION p2 VALUES LESS THAN (20),
PARTITION p3 VALUES LESS THAN (MAXVALUE));
Index created.
SQL> select index_name,partitioned, index_type from user_indexes where table_name='TEST_PARTITION';

INDEX_NAME PAR INDEX_TYPE
------------------------------ --- ---------------------------
TPID_G YES NORMAL

Related Documents
How to convert non-partitioned table to partition table using re-definition
Partitioning in Oracle- How to do partition
How to make partitioning in Oracle more Quickly

Thursday, October 30, 2008

How to avoid rebuild of indexes if DDL performed on partitioned table

On the partitioned table, if we don't do anything with the index then the index against the partitioned table remain global Non partitioned indexes and thus whenever we perform any DDL operation against the table partition like drop a partition or truncate a partition or add a partition or merge a partition or split a partition then the associated global indexes become invalid. Thus after doing DDL we need to rebuild the indexes. This may be very cumbersome and it may need several hours if the table is big enough.

With an example I am demonstrating how global indexes against a table become invalid if I do any DDL against the table.

1)Create a partitioned table.

CREATE TABLE test_partition
(
id number,
created_date date,
col3 varchar2(20)
)
PARTITION BY RANGE (created_date)
(
PARTITION part1 VALUES LESS THAN (to_date('11-oct-2008','dd-mon-yyyy')) ,
PARTITION part2 VALUES LESS THAN (to_date('12-oct-2008','dd-mon-yyyy')) ,
PARTITION part3 VALUES LESS THAN (to_date('13-oct-2008','dd-mon-yyyy')) ,
PARTITION part4 VALUES LESS THAN (to_date('14-oct-2008','dd-mon-yyyy')) ,
PARTITION junk VALUES LESS THAN (MAXVALUE)
)
/

Table created.

2)Insert data into the partitioned table.

insert into test_partition
select rownum, to_date('10-oct-2008','dd-mon-yyyy')+mod(rownum,7),
rownum
from dba_users;


41 rows created.

3)Create index on the partitioned table.
SQL> create index tpid_i on test_partition(id);
Index created.

4)Be sure partitioned is done successful.
SQL> select count(*) from test_partition partition (part2);

COUNT(*)
----------
6

Let's see some sample data.

SQL> select * from test_partition partition (part2);
ID CREATED_D COL3
---------- --------- --------------------
1 11-OCT-08 1
8 11-OCT-08 8
15 11-OCT-08 15
22 11-OCT-08 22
29 11-OCT-08 29
36 11-OCT-08 36

6 rows selected.


5)Let us see the status of the index against the partitioned table.
Note that since we have made global non-partitioned indexes so we will not get any entry about valid/unusable index in view user_ind_partitions. To get index status we have to query from user_indexes view.

SQL> select partition_name, status from user_ind_partitions where
index_name = 'TPID_I';

no rows selected


SQL> select index_name, status from user_indexes where table_name='TEST_PARTITION';

INDEX_NAME STATUS
------------------------------ --------
TPID_I VALID


6)Do some DDL operation on the partitioned table.
SQL> alter table test_partition truncate partition part2;
Table truncated.

Check by,
SQL> select count(*) from test_partition partition (part2);

COUNT(*)
----------
0

7)Check the index status now.

SQL> select index_name, status from user_indexes where table_name='TEST_PARTITION';

INDEX_NAME STATUS
------------------------------ --------
TPID_I UNUSABLE


Since we have global index so any DDL under the table marks the index unusable. If you want to make the index valid then you have to rebuild the index.

SQL> alter index tpid_i rebuild;
Index altered.



SQL> select index_name, status from user_indexes where table_name='TEST_PARTITION';

INDEX_NAME STATUS
------------------------------ --------
TPID_I VALID


This rebuild process really time consuming. And for production server it may not be tolerable.

8)The solution is to make the index as local partitioned index.
SQL> drop index tpid_i;
Index dropped.


SQL> create index tpid_i on test_partition(id) local;

Index created.

Check status by,


SQL> select index_name, status from user_indexes where table_name='TEST_PARTITION';

INDEX_NAME STATUS
------------------------------ --------
TPID_I N/A


Since it is local partitioned index we have to query from user_ind_partitions.
SQL> select partition_name, status from user_ind_partitions where
2 index_name = 'TPID_I';


PARTITION_NAME STATUS
------------------------------ --------
JUNK USABLE
PART1 USABLE
PART2 USABLE
PART3 USABLE
PART4 USABLE

SQL> select count(*) from test_partition partition (part3);
COUNT(*)
----------
6

9)Let's do some DDL operation and test.
SQL> alter table test_partition truncate partition part3;
Table truncated.


SQL> select index_name, status from user_indexes where table_name='TEST_PARTITION';

INDEX_NAME STATUS
------------------------------ --------
TPID_I N/A


SQL> select partition_name, status from user_ind_partitions where
2 index_name = 'TPID_I';

PARTITION_NAME STATUS
------------------------------ --------
JUNK USABLE
PART1 USABLE
PART2 USABLE
PART3 USABLE
PART4 USABLE

And it still remains in USABLE status. So you might able to save a lots of time as well as non-interrupt service and effort against it.

Monday, October 27, 2008

ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

Error Description:
The scenario is I have created a materialized view through database link over a table reside on the remote database.

I used the ON COMMIT option and it fails with ORA-12054 as below.

SQL> create materialized view scott.mv_phones
2 TABLESPACE users
3 REFRESH FAST on commit
4 WITH rowid
5 AS select ID, PASS,
6 CONN_EXPIRY_DATE, CONN_STATUS
7 from PHONES@lnxdb where upper(IS_SOLD)='Y';
from PHONES@lnxdb where upper(IS_SOLD)='Y'
*
ERROR at line 7:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

Cause of the Problem
The ON COMMIT clause of CREATE MATERIALIZE VIEW has some restrictions. Such as,

•ON COMMIT clause is not supported for materialized views containing object types.

•With ON COMMIT clause inserting into the materialized view table(master table) is not supported by selecting rows from remote tables.

In our case we satisfy the second restriction and hence error comes.

Solution of the Problem
To implement the solution you have to provide ON DEMAND clause. You have to create as below.
SQL> create materialized view scott.mv_phones
2 TABLESPACE users
3 REFRESH FAST on demand
4 WITH rowid
5 AS select ID, PASS,
6 CONN_EXPIRY_DATE, CONN_STATUS
7 from PHONES@lnxdb where upper(IS_SOLD)='Y';

My 600th post in this Blog.

This is the 600th post published in this blog. Could not publish blogs regularly now a days because I am a bit busy with my consultancy.

Hopefully I will write regularly. Pray for me.

Any suggestion and feedback from you is really welcome.