If you have a moving window partitioning scheme then you should know that in Oracle 11g you now have a brand new way of creating new partitions. Oracle 11g creates the partitions in an automatic fashion, so that when the row arrives at the table, if the partition does not exist, Oracle will create it. This is awsome, but imagine two things:
* You can’t migrate to Oracle 11g on the fly. You’re stuck in 10g
* You also need to drop the oldest partition, because you have lack of storage issues
Then if you are in this situation we have the solution for you. It’s bullet-proof tested and proven at production sites.
Every night you need to add another partition and remove the oldest. Pure moving window stuff. The format of the partition name is: PXXXX_YYYY_MM_DD, where XXXX is a sequential number that increments every day. This is daily partitioning and should only have a job running this code everynight:
declare
x varchar2(90);
s varchar2(900);
begin
-- Fetchs the name of the oldest partition
select partition_name
into x
from sys.dba_tab_partitions
where partition_position = 1
and table_name = 'MYTABLE'
and table_owner = 'MYOWNER';
-- Builds the name-string
s := 'ALTER TABLE MYOWNER.MYTABLE DROP PARTITION '||x||' UPDATE INDEXES';
-- Uses a customized report and sends it by email
-- so you can see the partitions state before and after
MYOWNER.my_pkg.sends_email('REPORT6');
-- Drops the Partition
execute immediate s;
-- And now adds another
MYOWNER.add_partition;
MYOWNER.my_pkg.sends_email('REPORT6');
--dbms_output.put_line(s);
end;
The procedure “add_partition” is another piece of automatic code that you must create prior to the previous PL/SQL block:
CREATE OR REPLACE procedure MYOWNER.add_partition is next_part varchar2(40); less_than_char varchar2(20); comando_add varchar2(1000); BEGIN
-- Generates the name of the partition
select 'P'||to_char(to_number(substr(partition_name,2,
instr(partition_name,'_',1)-2))+1)||'_'||
to_char(to_date(substr(partition_name,
instr(partition_name,'_',1)+1),'yyyy_mm_dd')+1,'yyyy_mm_dd'),
replace(to_char(to_date(substr(partition_name,
instr(partition_name,'_',1)+1),'yyyy_mm_dd')+2,'yyyy_mm_dd'),'_','-')
into next_part,less_than_char
from dba_tab_partitions
where table_owner = 'MYOWNER'
and table_name = 'MYTABLE'
and partition_position = (select max(partition_position)
from dba_tab_partitions where table_owner = 'MYOWNER'
and table_name = 'MYTABLE');
-- Builds the statement string
comando_add := 'ALTER TABLE MYOWNER.MYTABLE ADD PARTITION '||next_part;
comando_add := comando_add||' VALUES LESS THAN (to_date('||chr(39)||less_than_char;
comando_add := comando_add||chr(39)||','||chr(39)||
'yyyy-mm-dd'||chr(39)||')) TABLESPACE DATA_PARTITIONED';
-- Executes the statement
execute immediate(comando_add);
--dbms_output.put_line(comando_add);
end;
/
You will have permission issues that you can resolve reading this.
If you need any further help applying this, please let me know.
LMC.
Pingback: Blogroll Report 09/10/2009-16/10/2009 « Coskan’s Approach to Oracle
Hi..
Nice one!!! Will definitely try it out..What changes need to be done, if want to drop and add monthly partitions.
Regards,
Anand
Thanks Anand.
These scripts assume that the daily partitions of the table have the following format:
PXXXX_YYYY_MM_DD
where XXXX is a number, and the rest is a date mask. So if you want to adapt this to monthly I think that you just have to decide first on a format for your partition names. Assuming you don’t want to adapt too much my scripts, just wipe off the “_DD” part of format, and replace in the script “YYYY_MM_DD” with” YYYY_MM” and everything should work fine.
Cheers,
LMC.
Hi, this looked to be exactly what I needed (much appreciate that it’s visible on the web and thanks for sharing). However, on trying the create part, my SQLDevelper tool says it’s ignoring the statement:
select ‘P’||to_char(to_number(substr(partition_name,2,instr(partition_name,’_',1)-2))+1)||’_'||to_char(to_date(substr(partition_name,instr(partition_name,’_',1)+1),
then goes onto say my table does not exist at this point:
and partition_position = (select max(partition_position) from dba_tab_partitions where table_owner = ‘table_owner’ AND table_name = ‘table_name’);
(I tried the above section as a simple select statement and it returns as expected).
Admittedly, my SQL skills are a bit rusty.
Any suggestions would be greatly appreciated.
Cheers
Lex
I would check the quotes first, because it seems to be two of them in the statement. WordPress posts are hard to format and probably the fault is mine. Anyway, reduce everything to the same basic simple quote => ‘
Thanks for reading and commenting.
LMC.
i need similar type solution but for number of tables say 10 or 50 .how your script can be used?
Easy.
Transform the Anonymous block (the one that starts with DECLARE) into a procedure.
Where you read DECLARE substitute with:
create or replace procedure drop_and_create_partition (tabname varchar2)
Substitute the following block:
and table_name = ‘MYTABLE’
and table_owner = ‘MYOWNER’;
— Builds the name-string
s := ‘ALTER TABLE MYOWNER.MYTABLE DROP PARTITION ‘||x||’ UPDATE INDEXES’;
By this one:
and table_name = tabname
and table_owner = ‘MYOWNER’;
— Builds the name-string
s := ‘ALTER TABLE MYOWNER.’||tabname||’ DROP PARTITION ‘||x||’ UPDATE INDEXES’;
Then in the other procedure make the following adjustments:
Where you read:
CREATE OR REPLACE procedure MYOWNER.add_partition
You should code it like:
CREATE OR REPLACE procedure MYOWNER.add_partition (tabname varchar2)
And where you read:
and table_name = ‘MYTABLE’); (actually there are two lines like this)
You should code it like:
and table_name = tabname);
And where you read:
comando_add :=’ALTER TABLE MYOWNER.MYTABLE ADD PARTITION ‘||next_part;
You should code it like:
comando_add :=’ALTER TABLE MYOWNER.’||tabname||’ ADD PARTITION ‘||next_part;
And in the end just invoke:
SQL> exec drop_and_create_partition(‘Table1′);
SQL> exec drop_and_create_partition(‘Table2′);
SQL> exec drop_and_create_partition(‘Table3′);
(…)
SQL> exec drop_and_create_partition(‘Table50′);
Hope it helped.
LMC.
Hi,
Its really gud one.thanks for your inputs.
right now my scenraio is we have 100 tables which are monthly partioned,and management wanted to drop future partitions(say from april to dec partitons) in all the 100 tables and wanted to convert them as daily partitons.how do i need to go ahead.as am newbie to partitions.
your inputs are more valuable to me.
thanks
You can partition by Month and then subpartition by day.
If you want to read more about these features, please click “Go Tropical” in this page on the right top side and choose your database version.
There you’ll have lots of info to chew on for the next century
Cheers,
LMC:
thanks for your reply.
all the tables were already monthly partioned.
am not good enough in pl/sql so asked for converting into dauly partitions at one stretch.
thnaks in advance.
Without giving it a second thought I would say subpartitioned the table would be the closest to a “one-strech” operation you can get.
If someone out there has some other pre-cooked stuff it’s time to show the recipe.
You’re welcomed manjula.
LMC.
Hi Luis,
You have posted very useful article with automatic partition by days.
I have the table partition by hours, do you have any idea how to maintain hourly partition?
CREATE TABLE TXN2
(
DATETIME DATE NOT NULL,
IMSI NUMBER NOT NULL
)
PARTITION BY RANGE (DATETIME)
(
PARTITION P0000_2011_02_14_00 VALUES LESS THAN (to_date(’14.02.2011 00:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_01 VALUES LESS THAN (to_date(’14.02.2011 01:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_02 VALUES LESS THAN (to_date(’14.02.2011 02:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_03 VALUES LESS THAN (to_date(’14.02.2011 03:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_04 VALUES LESS THAN (to_date(’14.02.2011 04:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_05 VALUES LESS THAN (to_date(’14.02.2011 05:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_06 VALUES LESS THAN (to_date(’14.02.2011 06:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_07 VALUES LESS THAN (to_date(’14.02.2011 07:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_08 VALUES LESS THAN (to_date(’14.02.2011 08:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_09 VALUES LESS THAN (to_date(’14.02.2011 09:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_10 VALUES LESS THAN (to_date(’14.02.2011 10:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_11 VALUES LESS THAN (to_date(’14.02.2011 11:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_12 VALUES LESS THAN (to_date(’14.02.2011 12:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_13 VALUES LESS THAN (to_date(’14.02.2011 13:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_14 VALUES LESS THAN (to_date(’14.02.2011 14:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_15 VALUES LESS THAN (to_date(’14.02.2011 15:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_16 VALUES LESS THAN (to_date(’14.02.2011 16:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_17 VALUES LESS THAN (to_date(’14.02.2011 17:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_18 VALUES LESS THAN (to_date(’14.02.2011 18:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_19 VALUES LESS THAN (to_date(’14.02.2011 19:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_20 VALUES LESS THAN (to_date(’14.02.2011 20:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_21 VALUES LESS THAN (to_date(’14.02.2011 21:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_22 VALUES LESS THAN (to_date(’14.02.2011 22:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_14_23 VALUES LESS THAN (to_date(’14.02.2011 23:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_00 VALUES LESS THAN (to_date(’15.02.2011 00:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_01 VALUES LESS THAN (to_date(’15.02.2011 01:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_02 VALUES LESS THAN (to_date(’15.02.2011 02:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_03 VALUES LESS THAN (to_date(’15.02.2011 03:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_04 VALUES LESS THAN (to_date(’15.02.2011 04:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_05 VALUES LESS THAN (to_date(’15.02.2011 05:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_06 VALUES LESS THAN (to_date(’15.02.2011 06:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_07 VALUES LESS THAN (to_date(’15.02.2011 07:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_08 VALUES LESS THAN (to_date(’15.02.2011 08:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_09 VALUES LESS THAN (to_date(’15.02.2011 09:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_10 VALUES LESS THAN (to_date(’15.02.2011 10:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_11 VALUES LESS THAN (to_date(’15.02.2011 11:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_12 VALUES LESS THAN (to_date(’15.02.2011 12:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_13 VALUES LESS THAN (to_date(’15.02.2011 13:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_14 VALUES LESS THAN (to_date(’15.02.2011 14:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_15 VALUES LESS THAN (to_date(’15.02.2011 15:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_16 VALUES LESS THAN (to_date(’15.02.2011 16:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_17 VALUES LESS THAN (to_date(’15.02.2011 17:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_18 VALUES LESS THAN (to_date(’15.02.2011 18:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_19 VALUES LESS THAN (to_date(’15.02.2011 19:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_20 VALUES LESS THAN (to_date(’15.02.2011 20:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_21 VALUES LESS THAN (to_date(’15.02.2011 21:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_22 VALUES LESS THAN (to_date(’15.02.2011 22:00:00′,’dd.mm.syyyy hh24:mi:ss’)),
PARTITION P0000_2011_02_15_23 VALUES LESS THAN (to_date(’15.02.2011 23:00:00′,’dd.mm.syyyy hh24:mi:ss’))
)
)
thanks in advance.
hi, thanks for the code but when i execute the code…
declare
x varchar2(90);
s varchar2(900);
Begin
— Fetchs the name of the oldest partition
select partition_name
into x
from user_tab_partitions
where partition_position = 1
and table_name = ‘EMP3′
– and table_owner = ‘SCOTT’;
— Builds the name-string
s:=’ALTER TABLE EMP3 DROP PARTITION ‘ || x ||’ UPDATE INDEXES;
— Drops the Partition
execute immediate s;
end;
/
ERROR at line 13:
ORA-06550: line 13, column 2:
PL/SQL: ORA-00933: SQL command not properly ended
ORA-06550: line 6, column 2:
PL/SQL: SQL Statement ignored
i have ignored the
The is a character missing!
Where you have: s:=’ALTER TABLE EMP3 DROP PARTITION ‘ || x ||’ UPDATE INDEXES;
You should have: s:=’ALTER TABLE EMP3 DROP PARTITION ‘ || x ||’ UPDATE INDEXES’;
Hope it runs now.
LMC
Hi,
How to drop interval partitions by day
Very helpful!
Can you post the code for you partition report by any chance?
You would have to merge them first into the day unit, and then wipe them off or exchange partition with an empty table.
LMC
Sorry but I can’t because it stayed under the customer realm.
LMC
You are an oracle angel, Of course, this code can be used by trigger or scheduler. I just need to add a parttion, any cooked code for trigger?
I’m afraid not Bill. Nothing in the hoven either
To implement this as a trigger, it’s just a dangerous way of doing it, but if well done, might be the cherry on top of the cake.
If you sort it out please share.
Cheers,
LMC
My case is partition by numeric range, and next number is created by a sequencer. I’m think when sequencer is called then trigger the add_partition, I need to figure out the trigger, seq.nextval –>add_partiotion, any idea?
OK, I create a dummy table once I have the nextval, I insert into the table which has a trigger after insertion, any better idea?