Skip to Main Content

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Chakra.

Asked: August 14, 2012 - 2:29 am UTC

Last updated: March 01, 2022 - 2:08 pm UTC

Version: 11.2

Viewed 50K+ times! This question is

You Asked

Hi Tom,
I am trying to create a partitioned table so that a date-wise partition is created on inserting a new row for release_date column.
But please note that release_date column is having number data type (as per design) and people want to create an interval based partition on this.

Any work around for this?

They want data type *NOT* to be altered.

create table product(
prod_id number,
prod_code varchar2(3),
release_date number)
partition by range(to_date(release_date,'YYYYMMDD'))
interval(numtodsinterval(1,'day'))
(partition p0 values less than (to_date('02-AUG-2012','DD-MON-YYYY')),
partition p1 values less than (to_date('03-AUG-2012','DD-MON-YYYY'))
)

Thanks for your time.

and Tom said...

Do not use a date in the partitioning scheme then, just use a number with interval 1.

ops$tkyte%ORA11GR2> create table product
  2  ( prod_id number,
  3    prod_code varchar2(3),
  4    release_date number
  5  )
  6  partition by range(release_date)
  7  interval(1)
  8  (partition empty values less than (20120101) )
  9  /

Table created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> insert into product (prod_id,prod_code,release_date) values ( 1, 'a', 20120315 );

1 row created.

ops$tkyte%ORA11GR2> insert into product (prod_id,prod_code,release_date) values ( 1, 'a', 20120625 );

1 row created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> select partition_name, high_value
  2    from user_tab_partitions
  3   where table_name = 'PRODUCT'
  4  /

PARTITION_NAME       HIGH_VALUE
-------------------- ----------
EMPTY                20120101
SYS_P1723            20120316
SYS_P1724            20120626


Rating

  (16 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Monthly partition is also possible/comment only

anonymous, August 17, 2012 - 8:06 am UTC

And if You want a monthly partition, then use:

interval(100)






A reader, February 06, 2013 - 3:31 am UTC

But what about the interval when there is a change in year? If we have partition created till 20121201, then next partition high value should be 20130101. In this case the difference will be 8900. So I believe the next partition after 20121201 will be 20121301 which could not be the valid month. Any workaround for this.
Tom Kyte
February 06, 2013 - 1:55 pm UTC

8900 what?

I don't know what you mean at all here.


If you have monthly partitions, we add months - not days or hours or anything - you can add a month to 01-dec-2012 resulting in 01-jan-2013.

Problem when year changes

Shobhank, March 13, 2013 - 10:07 am UTC

Scenario: Monthly partitioning on DateID (YYYYMMDD):
Partition interval = 100
1st Partition DateID < 19990101
2nd Partition DateID < 19990201
3rd Partition DateID < 19990301
:
:
:
nth Partition DateID < 19991201
(n+1)th Partition DateID < 19991301

Which is wrong; 13th month?
Next partition should be 20000101
which has interval = 8900
Just to restate the above person's correct point!

Tom Kyte
March 13, 2013 - 10:42 am UTC

but that wouldn't be a problem with *my* solution with the interval of 1.

and it wouldn't be an issue with using 100 either.

think about it, interval partitioning only creates partitions when the data is inserted with a value. it doesn't create empty partitions.

So, if you insert 19991201 it'll create a partition and put anything with the values 19991201 up to but not including 19991301 into it (but you'll never put 19991300 in there - that isn't a date)

when you put 20000101 in there, it'll create a partition for 20000101 up to but not including 20000201.

it'll work dandy.


ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> create table product
  2  ( prod_id number,
  3    prod_code varchar2(3),
  4    release_date number
  5  )
  6  partition by range(release_date)
  7  interval(100)
  8  (partition empty values less than (19990101) )
  9  /

Table created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> insert into product (prod_id, prod_code, release_date )
  2  select rownum, rownum, to_char( add_months( to_date('19990101','yyyymmdd'), rownum ),'yyyymmdd' )
  3    from dual
  4   connect by level <= 20;

20 rows created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> create table tmp
  2  as
  3  select table_name, partition_name, to_lob(high_value) high_value
  4    from user_tab_partitions
  5   where table_name = 'PRODUCT';

Table created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> select t.*,
  2         (select release_date
  3                from product
  4                   where release_date >= xfrom
  5                     and release_date <  xto) val
  6    from (
  7  select table_name, partition_name, to_number(high_value)-100 xfrom, to_number(high_value) xto
  8    from tmp
  9         ) t;

TABLE_NA PARTITION_      XFROM        XTO        VAL
-------- ---------- ---------- ---------- ----------
PRODUCT  EMPTY        19990001   19990101
PRODUCT  SYS_P2657    19990201   19990301   19990201
PRODUCT  SYS_P2658    19990301   19990401   19990301
PRODUCT  SYS_P2659    19990401   19990501   19990401
PRODUCT  SYS_P2660    19990501   19990601   19990501
PRODUCT  SYS_P2661    19990601   19990701   19990601
PRODUCT  SYS_P2662    19990701   19990801   19990701
PRODUCT  SYS_P2663    19990801   19990901   19990801
PRODUCT  SYS_P2664    19990901   19991001   19990901
PRODUCT  SYS_P2665    19991001   19991101   19991001
PRODUCT  SYS_P2666    19991101   19991201   19991101
PRODUCT  SYS_P2667    19991201   19991301   19991201
PRODUCT  SYS_P2668    20000101   20000201   20000101
PRODUCT  SYS_P2669    20000201   20000301   20000201
PRODUCT  SYS_P2670    20000301   20000401   20000301
PRODUCT  SYS_P2671    20000401   20000501   20000401
PRODUCT  SYS_P2672    20000501   20000601   20000501
PRODUCT  SYS_P2673    20000601   20000701   20000601
PRODUCT  SYS_P2674    20000701   20000801   20000701
PRODUCT  SYS_P2675    20000801   20000901   20000801
PRODUCT  SYS_P2676    20000901   20001001   20000901

21 rows selected.


interval partition on date

Sridhar, April 23, 2013 - 11:29 am UTC

Interval partition on date behaves strange in DEV environment .

select * from fct_lm_all where effective_day = '29-Feb-2012'

Effective_day is a DATE type and the partitioned column.

This query works well in PROD and UAT but not in development . This query needs a trunc(effective_day) in development to return any rows. Any help on what could be different in DEV is appreciated .

Thanks
Sridhar
Tom Kyte
April 23, 2013 - 1:16 pm UTC

do this,

alter session set nls_date_format = 'dd-mon-yyyy hh24:mi:ss';


in all situations and see what dates you have in there.

I'll bet you a dollar that in development - you have "time" on your dates and in the other environments you do not.


also, NEVER CODE

select * from fct_lm_all where effective_day = '29-Feb-2012'


do this:

select * from fct_lm_all where effective_day = to_date('29-Feb-2012','dd-mon-yyyy')


always use an explicit date mask converting strings to dates and dates to strings!!!



A reader, April 24, 2013 - 10:06 am UTC

Correct . we alway need to qualify date filter with to_date as a practice.

However such date filters ( day = '29-Feb-2012' ) work fine on all other tables in the database
except this table

The only difference we see is that this is an interval partitioned table on date.
Will this make any difference ?

Alter session didnt work. And this parameter is same ( dd-mon-yyyy ) in all 3 environments.

Tom Kyte
April 24, 2013 - 1:05 pm UTC

the alter session was so you could see the data fully.


ops$tkyte%ORA11GR2> set echo on
ops$tkyte%ORA11GR2> select created from all_users where rownum < 5;

CREATED
---------
05-SEP-10
05-SEP-10
05-SEP-10
05-SEP-10

ops$tkyte%ORA11GR2> @bigdate
ops$tkyte%ORA11GR2> alter session set nls_date_format = 'dd-mon-yyyy hh24:mi:ss';

Session altered.

ops$tkyte%ORA11GR2> select created from all_users where rownum < 5;

CREATED
--------------------
05-sep-2010 15:40:00
05-sep-2010 15:40:00
05-sep-2010 15:40:02
05-sep-2010 15:41:22




when you do that with YOUR data - does the data show a time or does the data show 00:00:00 for the time.


what I suspect is the date column in your one non-functioning database has TIME on it. It does not have midnight. therefore it is not equal to 29-feb-2012 (which has 00:00:00 associated with it)


do this:

select count(*) from table where date_column = to_date('29-feb-2012','dd-mon-yyyy');

select count(*) from table where date_column between
to_date('29-feb-2012','dd-mon-yyyy') and
to_date('29-feb-2012','dd-mon-yyyy')+1-1/24/60/60;


post the results. if one returns 0 and the other returns non-zero, then you have time on your date - you data is botched.\





and frankly, I don't care if your default is currently dd-mon-yyyy - USE EXPLICIT FORMATS WITH DATES on to_date, on to_char. you are subject to sql injection bugs, you are subject to serious logic bombs otherwise.

A reader, April 26, 2013 - 10:09 am UTC

Please review the results below . I am not getting the result unless i trunc on effective_day . ( issue only in DEV )

I have checked that all rows have 00:00:00 time in the column.

SQL> select count(*) from fct_lm_all  where effective_day = to_date('29-feb-2012','dd-mon-yyyy'); 

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

SQL> select count(*) from fct_lm_all  where trunc(effective_day) = to_date('29-feb-2012','dd-mon-yyyy');

  COUNT(*)
----------
     19911

SQL>  select count(*) from fct_lm_all  where effective_day between to_date('29-feb-2012','dd-mon-yyyy') and 
    to_date('29-feb-2012','dd-mon-yyyy')+1-1/24/60/60; 

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

SQL> select count(*) from fct_lm_all  where trunc(effective_day) between 
    to_date('29-feb-2012','dd-mon-yyyy') and 
    to_date('29-feb-2012','dd-mon-yyyy')+1-1/24/60/60; 
 
  COUNT(*)
----------
     19911
  
SQL> select count(*) from fct_lm_all  where effective_day
  = to_date('31-Jan-2012 00:00:00','dd-mon-yyyy hh24:mi:ss');
  
   COUNT(*)
----------
     0
  
 SQL> select count(*) from fct_lm_all  where trunc(effective_day)
  = to_date('31-Jan-2012 00:00:00','dd-mon-yyyy hh24:mi:ss');
  
  COUNT(*)
----------
     20698

Tom Kyte
April 26, 2013 - 1:55 pm UTC

can you please select out the date column using dd-mon-yyyy hh24:mi:ss (which I've asked you to do a few times now) so we can see the data?


do this for us:


select to_char( effective_day, 'dd-mon-yyyy hh24:mi:ss' ), dump( effective_day )
from fct_lm_all
where trunc(effective_day) =
to_date('29-feb-2012','dd-mon-yyyy')
and rownum <= 25;


A reader, April 26, 2013 - 2:58 pm UTC

TO_CHAR(EFFECTIVE_DAY,'DD-MON- DUMP(EFFECTIVE_DAY)
1 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
2 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
3 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
4 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
5 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
6 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
7 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
8 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
9 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
10 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
11 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
12 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
13 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
14 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
15 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
16 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
17 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
18 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
19 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
20 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
21 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
22 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
23 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
24 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1
25 29-feb-2012 00:00:00 Typ=12 Len=7: 120,112,2,29,1,1,1

Tom Kyte
April 26, 2013 - 4:30 pm UTC

ahh, I suspect a busted index.

do one of your plans have an index range scan and the other a full scan?

can you:

analyze index index_name validate structure;

(that'll lock it - be aware of that).

Serializable Transaction on Interval Partitioned tables

Rajeshwaran Jeyabal, May 01, 2013 - 12:07 pm UTC

Tom:

I was reading VLDB guide on "When to Use Interval Partitioning"
<quote>
Serializable transactions do not work with interval partitioning. Inserting data into a partition of an interval partitioned table that does not have a segment yet causes an error.
</quote>

I don't see the above statement happens. I am on 11.2.0.2. do you see any flaw in my test case?
create table t
( x int,
  y varchar2(20) )
partition by range(x)
interval (1)
( partition p0 values less than (0) ); 
insert into t(x,y) values(1,'a');
commit;
rajesh@ORA11G> alter session set isolation_level=serializable;

Session altered.
rajesh@ORA11G> insert into t(x,y) values(2,'a');

1 row created.
rajesh@ORA11G> commit;

Commit complete.
rajesh@ORA11G>

http://docs.oracle.com/cd/E11882_01/server.112/e25523/part_avail.htm#BJEFIFCE
Tom Kyte
May 06, 2013 - 3:39 pm UTC

i've filed a doc issue against this. they say the same thing about deferred segment creation and that isn't true either.

Interval partitioning on sub partitions

satish, July 22, 2013 - 11:38 am UTC

Is it possible to create sub-partitions as interval partitions? I have table which is list partitioned and each partition sub partitioned by date range.We want to know if those range partitions can be created automatically using interval option.
Tom Kyte
July 31, 2013 - 4:21 pm UTC

http://docs.oracle.com/cd/E11882_01/server.112/e25523/part_admin001.htm#sthref371

the interval is part of the partition by syntax, not subpartition by.

A reader, July 25, 2016 - 8:24 am UTC

How to partition a table in interval on a column which does not contain consequence number like suppose talk about empa-id is like 1 2 3 and so on. but i want to interval partition on emp-id in my case is 1 30 73 190 200 7883 398489 .... so on.
Connor McDonald
July 25, 2016 - 6:59 pm UTC

That doesn't sound like a particularly useful column to partition by (perhaps look at hash partitioning), but if you really wanted to, you simply choose a sensible range, eg

partition by range ( empid)
interval (50000)

to have ranges of employee numbers of size n to n+50000


A reader, July 26, 2016 - 5:21 pm UTC

Does partition key contain null value?

Because I have table contain only 3 value of a column like null,1 and 2. Which is flag value.

Chris Saxon
July 26, 2016 - 8:41 pm UTC

Partition keys can be null - depending on how you've defined your partitions!

SQL> create table t (
  2    x int
  3  ) partition by range (x) (
  4    partition p0 values less than (10)
  5  );

Table created.

SQL> insert into t values (null);
insert into t values (null)
            *
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition


SQL>
SQL> drop table t purge;

Table dropped.

SQL> create table t (
  2    x int
  3  ) partition by range (x) (
  4    partition p0 values less than (10),
  5    partition pmax values less than (maxvalue)
  6  );

Table created.

SQL> insert into t values (null);

1 row created.

SQL>
SQL> drop table t purge;

Table dropped.

SQL> create table t (
  2    x int
  3  ) partition by list (x) (
  4    partition p0 values (0)
  5  );

Table created.

SQL> insert into t values (null);
insert into t values (null)
            *
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition


SQL>
SQL> drop table t purge;

Table dropped.

SQL> create table t (
  2    x int
  3  ) partition by list (x) (
  4    partition p0 values (0, null)
  5  );

Table created.

SQL> insert into t values (null);

1 row created.

SQL>
SQL>
SQL> drop table t purge;

Table dropped.

SQL> create table t (
  2    x int
  3  ) partition by hash (x) partitions 2;

Table created.

SQL> insert into t values (null);

1 row created.

Interval + Reference partition in 12c

Rajeshwaran, Jeyabal, September 02, 2019 - 12:00 pm UTC

Team,

was reading about Interval + Reference partition in Oracle 12c from docs @ https://docs.oracle.com/en/database/oracle/oracle-database/12.2/vldbg/partition-create-tables-indexes.html#GUID-52397EF9-EC3B-4E80-8211-77E683263530

the below demo was from 12.2

demo@PDB1> create table p(
  2     order_id number primary key,
  3     order_date date,
  4     data varchar2(30) )
  5  enable row movement
  6  partition by range(order_date)
  7             interval( numtoyminterval(1,'month') )
  8  ( partition p0 values less than
  9             (to_date('01-jan-2012','dd-mon-yyyy')) );

Table created.

demo@PDB1> create table c1(
  2     order_id number,
  3     line_id number,
  4     data varchar2(30),
  5     constraint c1_pk primary key(order_id,line_id),
  6     constraint c1_fk foreign key(order_id)
  7             references p )
  8  enable row movement
  9  partition by reference(c1_fk) ;

Table created.

demo@PDB1> create table c2(
  2     order_id number,
  3     line_id number,
  4     sub_line_id number,
  5     data varchar2(30),
  6     constraint c2_pk primary key(order_id,line_id,sub_line_id),
  7     constraint c2_fk foreign key(order_id,line_id)
  8             references c1 )
  9  enable row movement
 10  partition by reference(c2_fk) ;
partition by reference(c2_fk)
                       *
ERROR at line 10:
ORA-14652: reference partitioning foreign key is not supported


demo@PDB1>

Is it not possible to create Interval + Reference partition on more than on PK-FK hierarchy? Kindly advice.

Chris Saxon
September 02, 2019 - 2:52 pm UTC

All the FK columns must be NOT NULL when using reference partitioning. The second child table doesn't appear to be picking up the mandatory-ness of these columns from the primary key for some reason.

Explicitly declaring them as NOT NULL does the trick for me:

create table p (
   order_id number primary key,
   order_date date,
   data varchar2(30) 
)
enable row movement
partition by range(order_date)
  interval( numtoyminterval(1,'month') ) ( 
  partition p0 values less than (
    to_date('01-jan-2012','dd-mon-yyyy')
  ) 
);

create table c1(
   order_id number,
   line_id number,
   data varchar2(30),
   constraint c1_pk primary key(order_id,line_id),
   constraint c1_fk foreign key(order_id)
     references p 
)
enable row movement
partition by reference(c1_fk) ;

create table c2(
   order_id    number not null,
   line_id     number not null,
   sub_line_id number,
   data varchar2(30),
   constraint c2_pk 
     primary key(order_id,line_id,sub_line_id),
   constraint c2_fk 
     foreign key(order_id,line_id)
     references c1 
)
enable row movement
partition by reference(c2_fk) ;

select partition_name 
from   user_tab_partitions
where  table_name = 'C2';

PARTITION_NAME   
P0    

Exchange cascade on interval + reference partition tables

Rajeshwaran, Jeyabal, September 03, 2019 - 9:22 am UTC

Thanks the above works.

However the documentation https://docs.oracle.com/en/database/oracle/oracle-database/12.2/vldbg/maintenance-partition-tables-indexes.html#GUID-93BFB499-39A6-4DDE-88C3-BF7A63848E25
has this
<quote>
....
The exchange operation can be targeted at any level in a reference partitioned hierarchy and cascades to child tables starting from the targeted table.
....
</quote>
Here is my testcase from 12.2 that got error. could you help us to understand, what is missing/mismatch with FK on constraints?
demo@PDB1> create table p(
  2     order_id number primary key,
  3     order_date date,
  4     data varchar2(30) )
  5  partition by range(order_date)
  6             interval( numtoyminterval(1,'month') )
  7  ( partition p0 values less than
  8             (to_date('01-jan-2012','dd-mon-yyyy')) );

Table created.

demo@PDB1> create table c1(
  2     order_id number,
  3     line_id number,
  4     data varchar2(30),
  5     constraint c1_pk primary key(order_id,line_id),
  6     constraint c1_fk foreign key(order_id)
  7             references p )
  8  partition by reference(c1_fk) ;

Table created.

demo@PDB1> create table c2(
  2     order_id number not null,
  3     line_id number not null,
  4     sub_line_id number,
  5     data varchar2(30),
  6     constraint c2_pk primary key(order_id,line_id,sub_line_id),
  7     constraint c2_fk foreign key(order_id,line_id)
  8             references c1
  9             on delete cascade )
 10  partition by reference(c2_fk) ;

Table created.

demo@PDB1> create table p_temp(
  2     order_id number primary key,
  3     order_date date,
  4     data varchar2(30) );

Table created.

demo@PDB1>
demo@PDB1> create table c1_temp(
  2     order_id number,
  3     line_id number,
  4     data varchar2(30),
  5     constraint c1_temp_pk primary key(order_id,line_id),
  6     constraint c1_temp_fk foreign key(order_id)
  7             references p_temp );

Table created.

demo@PDB1> create table c2_temp(
  2     order_id number not null,
  3     line_id number not null,
  4     sub_line_id number,
  5     data varchar2(30),
  6     constraint c2_temp_pk primary key(order_id,line_id,sub_line_id),
  7     constraint c2_temp_fk foreign key(order_id,line_id)
  8             references c1_temp
  9             on delete cascade) ;

Table created.

demo@PDB1> select table_name,partition_name,interval
  2  from user_tab_partitions
  3  where table_name in ('P','C1','C2')
  4  order by 1,2;

TABLE_NAME PARTITION_ INT
---------- ---------- ---
C1         P0         NO
C2         P0         NO
P          P0         NO

demo@PDB1> insert into p(order_id,order_date,data)
  2     values(1,to_date('17-aug-2019','dd-mon-yyyy'),'*');

1 row created.

demo@PDB1> insert into c1(order_id,line_id,data)
  2     values(1,1,'*');

1 row created.

demo@PDB1> insert into c2(order_id,line_id,sub_line_id,data)
  2     values(1,1,1,'*');

1 row created.

demo@PDB1> select table_name,partition_name,interval
  2  from user_tab_partitions
  3  where table_name in ('P','C1','C2')
  4  order by 1,2;

TABLE_NAME PARTITION_ INT
---------- ---------- ---
C1         P0         NO
C1         SYS_P3548  YES
C2         P0         NO
C2         SYS_P3548  YES
P          P0         NO
P          SYS_P3548  YES

6 rows selected.

demo@PDB1> alter table c1
  2  exchange partition SYS_P3548
  3  with table c1_temp
  4  CASCADE ;
alter table c1
*
ERROR at line 1:
ORA-14128: FOREIGN KEY constraint mismatch in ALTER TABLE EXCHANGE PARTITION


demo@PDB1>

Chris Saxon
September 03, 2019 - 12:45 pm UTC

The FK of the table you're exchanging needs to point to the parent partitioned table:

create table p(
   order_id number primary key,
   order_date date,
   data varchar2(30)
) partition by range(order_date)
  interval( numtoyminterval(1,'month') ) ( 
    partition p0 values less than
           (to_date('01-jan-2012','dd-mon-yyyy')) 
);

create table c1(
   order_id number not null,
   line_id number not null,
   data varchar2(30),
   constraint c1_pk primary key(order_id,line_id),
   constraint c1_fk foreign key(order_id)
           references p
           on delete cascade
)
partition by reference(c1_fk) ;

create table c2(
   order_id number not null,
   line_id number not null,
   sub_line_id number,
   data varchar2(30),
   constraint c2_pk primary key(order_id,line_id,sub_line_id),
   constraint c2_fk foreign key(order_id,line_id)
           references c1
           on delete cascade )
partition by reference(c2_fk) ;

create table c1_temp(
   order_id number not null,
   line_id number  not null,
   data varchar2(30),
   constraint c1_temp_pk primary key(order_id,line_id),
   constraint c1_temp_fk foreign key(order_id)
           references p
           on delete cascade 
);

create table c2_temp(
   order_id number not null,
   line_id number not null,
   sub_line_id number,
   data varchar2(30),
   constraint c2_temp_pk primary key(order_id,line_id,sub_line_id),
   constraint c2_temp_fk foreign key(order_id,line_id)
           references c1_temp
           on delete cascade
) ;

select table_name,partition_name,interval
from user_tab_partitions
where table_name in ('P','C1','C2')
order by 1,2;

insert into p(order_id,order_date,data)
 values(1,to_date('17-aug-2019','dd-mon-yyyy'),'*');

insert into c1(order_id,line_id,data)
 values(1,1,'*');

insert into c2(order_id,line_id,sub_line_id,data)
 values(1,1,1,'*');

select table_name,partition_name,interval
from user_tab_partitions
where table_name in ('P','C1','C2')
order by 1,2;

TABLE_NAME    PARTITION_NAME    INTERVAL   
C1            P0                NO          
C1            SYS_P9274         YES         
C2            P0                NO          
C2            SYS_P9274         YES         
P             P0                NO          
P             SYS_P9274         YES  

alter table c1
exchange partition SYS_P9274
with table c1_temp
cascade
update indexes;

select * from c1_temp;

ORDER_ID    LINE_ID    DATA   
          1          1 *  

select * from c2_temp;

ORDER_ID    LINE_ID    SUB_LINE_ID    DATA   
          1          1              1 * 

About not to use a date in the partitioning scheme

Shane, October 08, 2019 - 8:54 am UTC

Hi Tom,

Why you said that "Do not use a date in the partitioning scheme then, just use a number with interval 1."? Is there any concerns about using date or timestamp as the partition key?

Thank you very much.
Chris Saxon
October 08, 2019 - 9:50 am UTC

Sure you can use date/timestamp for interval partitioning.

But read the question:

But please note that release_date column is having number data type (as per design)

...

They want data type *NOT* to be altered.

Interval Partitioning is not splitting partition after the reset of INTERVAL

Venkat, March 01, 2022 - 9:07 am UTC

I have a scenario where interval partition is not creating the MONTH partitions after the reset of INTERVAL to null and setting it back again to MONTH(which is done during the the purging of the older partitions) for the rows updated/inserted in the existing partition. Could you show some light on this.? Am I missing anything here? Is there a way to resolve this on a live table without outage?

Appreciate your valuable comments

SQL> SELECT BANNER FROM V$VERSION;

BANNER
----------------------------------------------------------------------------------------------------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
PL/SQL Release 12.2.0.1.0 - Production
CORE 12.2.0.1.0 Production
TNS for Linux: Version 12.2.0.1.0 - Production
NLSRTL Version 12.2.0.1.0 - Production

SQL> CREATE TABLE tab_prtn_1(col1 number, col2 date, col3 date)
2 PARTITION BY RANGE(col2)
3 interval (NUMTOYMINTERVAL(1,'MONTH'))
4 (
5 PARTITION VALUES LESS THAN (TO_DATE(' 2022-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
6 )
7 enable row movement;

Table created.

SQL> insert into tab_prtn_1(col1, col2, col3) values (1, sysdate, sysdate+5);

1 row created.

SQL> commit;

Commit complete.

SQL>
SQL> Select table_name, partition_name, high_value, high_value_length, partition_position, initial_extent, next_extent From User_tab_partitions Where Table_name='TAB_PRTN_1';

TABLE_NAME PARTITION_NAME HIGH_VALUE HIGH_VALUE_LENGTH PARTITION_POSITION INITIAL_EXTENT NEXT_EXTENT
---------- --------------- -------------------------------------------------------------------------------- ----------------- ------------------ -------------- -----------
TAB_PRTN_1 SYS_P1463991 TO_DATE(' 2022-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 1
TAB_PRTN_1 SYS_P1463992 TO_DATE(' 2022-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 2 8388608 1048576

SQL> Update tab_prtn_1
2 set col2 = '01-Feb-2022';

1 row updated.

SQL> commit;

Commit complete.

SQL>
SQL> Select table_name, partition_name, high_value, high_value_length, partition_position, initial_extent, next_extent From User_tab_partitions Where Table_name='TAB_PRTN_1';

TABLE_NAME PARTITION_NAME HIGH_VALUE HIGH_VALUE_LENGTH PARTITION_POSITION INITIAL_EXTENT NEXT_EXTENT
---------- --------------- -------------------------------------------------------------------------------- ----------------- ------------------ -------------- -----------
TAB_PRTN_1 SYS_P1463991 TO_DATE(' 2022-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 1
TAB_PRTN_1 SYS_P1463993 TO_DATE(' 2022-03-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 2 8388608 1048576
TAB_PRTN_1 SYS_P1463992 TO_DATE(' 2022-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 3 8388608 1048576

SQL>
SQL>
SQL> insert into tab_prtn_1(col1, col2, col3) values (1, add_months(trunc(sysdate),5), sysdate+5);

1 row created.

SQL> commit;

Commit complete.

SQL> select col2, count(1) from tab_prtn_1 group by col2;

COL2 COUNT(1)
-------------------- ----------
01-FEB-22 1
01-AUG-22 1

SQL> Select table_name, partition_name, high_value, high_value_length, partition_position, initial_extent, next_extent From User_tab_partitions Where Table_name='TAB_PRTN_1';

TABLE_NAME PARTITION_NAME HIGH_VALUE HIGH_VALUE_LENGTH PARTITION_POSITION INITIAL_EXTENT NEXT_EXTENT
---------- --------------- -------------------------------------------------------------------------------- ----------------- ------------------ -------------- -----------
TAB_PRTN_1 SYS_P1463991 TO_DATE(' 2022-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 1
TAB_PRTN_1 SYS_P1463993 TO_DATE(' 2022-03-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 2 8388608 1048576
TAB_PRTN_1 SYS_P1463992 TO_DATE(' 2022-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 3 8388608 1048576
TAB_PRTN_1 SYS_P1463994 TO_DATE(' 2022-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 4 8388608 1048576

SQL> ALTER TABLE TAB_PRTN_1 SET INTERVAL();

Table altered.

SQL>
SQL> ALTER TABLE TAB_PRTN_1 SET INTERVAL(NUMTOYMINTERVAL(1,'MONTH'));

Table altered.

SQL> Update tab_prtn_1
2 set col2 = '01-Jun-2022'
3 Where trunc(col2) = '01-Aug-2022';

1 row updated.

SQL> commit;

Commit complete.

SQL> select col2, count(1) from tab_prtn_1 group by col2;

COL2 COUNT(1)
-------------------- ----------
01-FEB-22 1
01-JUN-22 1

SQL>
SQL> Select table_name, partition_name, high_value, high_value_length, partition_position, initial_extent, next_extent From User_tab_partitions Where Table_name='TAB_PRTN_1';

TABLE_NAME PARTITION_NAME HIGH_VALUE HIGH_VALUE_LENGTH PARTITION_POSITION INITIAL_EXTENT NEXT_EXTENT
---------- --------------- -------------------------------------------------------------------------------- ----------------- ------------------ -------------- -----------
TAB_PRTN_1 SYS_P1463991 TO_DATE(' 2022-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 1
TAB_PRTN_1 SYS_P1463993 TO_DATE(' 2022-03-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 2 8388608 1048576
TAB_PRTN_1 SYS_P1463992 TO_DATE(' 2022-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 3 8388608 1048576
TAB_PRTN_1 SYS_P1463994 TO_DATE(' 2022-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 4 8388608 1048576

SQL>
SQL>
SQL> insert into tab_prtn_1(col1, col2, col3) values (1, add_months(trunc(sysdate),3), sysdate+5);

1 row created.

SQL> commit;

Commit complete.

SQL> Select table_name, partition_name, high_value, high_value_length, partition_position, initial_extent, next_extent From User_tab_partitions Where Table_name='TAB_PRTN_1';

TABLE_NAME PARTITION_NAME HIGH_VALUE HIGH_VALUE_LENGTH PARTITION_POSITION INITIAL_EXTENT NEXT_EXTENT
---------- --------------- -------------------------------------------------------------------------------- ----------------- ------------------ -------------- -----------
TAB_PRTN_1 SYS_P1463991 TO_DATE(' 2022-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 1
TAB_PRTN_1 SYS_P1463993 TO_DATE(' 2022-03-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 2 8388608 1048576
TAB_PRTN_1 SYS_P1463992 TO_DATE(' 2022-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 3 8388608 1048576
TAB_PRTN_1 SYS_P1463994 TO_DATE(' 2022-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 4 8388608 1048576

SQL> select col2, count(1) from tab_prtn_1 group by col2;

COL2 COUNT(1)
-------------------- ----------
01-FEB-22 1
01-JUN-22 2

SQL> insert into tab_prtn_1(col1, col2, col3) values (1, add_months(trunc(sysdate),4), sysdate+5);

1 row created.

SQL> commit;

Commit complete.

SQL> Select table_name, partition_name, high_value, high_value_length, partition_position, initial_extent, next_extent From User_tab_partitions Where Table_name='TAB_PRTN_1';

TABLE_NAME PARTITION_NAME HIGH_VALUE HIGH_VALUE_LENGTH PARTITION_POSITION INITIAL_EXTENT NEXT_EXTENT
---------- --------------- -------------------------------------------------------------------------------- ----------------- ------------------ -------------- -----------
TAB_PRTN_1 SYS_P1463991 TO_DATE(' 2022-02-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 1
TAB_PRTN_1 SYS_P1463993 TO_DATE(' 2022-03-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 2 8388608 1048576
TAB_PRTN_1 SYS_P1463992 TO_DATE(' 2022-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 3 8388608 1048576
TAB_PRTN_1 SYS_P1463994 TO_DATE(' 2022-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA 83 4 8388608 1048576

SQL> select col2, count(1) from tab_prtn_1 group by col2;

COL2 COUNT(1)
-------------------- ----------
01-FEB-22 1
01-JUL-22 1
01-JUN-22 2

SQL> SPOOL OFF
Chris Saxon
March 01, 2022 - 2:08 pm UTC

When you set an empty interval, the database converts all existing interval partitions to be non-interval partitions:

create table t (
  c1 int, c2 date
) partition by range ( c2 )
  interval  ( interval '1' month ) (
    partition p0 values less than ( date'2022-01-01' )
  );

insert into t values ( 1, sysdate );

select partition_name, interval, high_value
from   user_tab_partitions 
where  table_name='T';

PARTITION_ INT HIGH_VALUE                                                                      
---------- --- --------------------------------------------------------------------------------
P0         NO  TO_DATE(' 2022-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA
SYS_P10141 YES TO_DATE(' 2022-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA

alter table t set interval ();
alter table t set interval ( interval '1' month );

select partition_name, interval, high_value
from   user_tab_partitions 
where  table_name='T';

PARTITION_ INT HIGH_VALUE                                                                      
---------- --- --------------------------------------------------------------------------------
P0         NO  TO_DATE(' 2022-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA
SYS_P10141 NO  TO_DATE(' 2022-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA


Because they're NOT interval partitions anymore, the database doesn't split them when you insert new values. You have to do this manually.

You can do this online from 12.2:

alter table ... 
  split partition ...
  online


the reset of INTERVAL to null and setting it back again to MONTH

Why are you doing this?

Venkat, March 01, 2022 - 4:44 pm UTC

Dear Chris,

Thanks a lot.

I was analyzing it and could not find the rationale behind the implementation setting it to null.

Instead of doing the splitting manually (using online approach), was thinking of any short ways to revert the table back to it old state (interval partition) as it is already in live.


More to Explore

VLDB

If you are new to partitioning, check out Connor McDonald's introduction series here.

VLDB

Documentation set on VLDB and Partitioning.