Interval-Reference-Partitioning: Partition-Merge and Row-Movement

As often, I use this blog to answer questions of training participants. This time, it’s the question what happens on interval-reference-partitioned tables when merging partitions or when updates cause row movement.

Our example is a typical use case for interval-reference partitioning: master table with orders (partitioned by order date(month)) and a second table with line items,

TVD12@TVD12> create table orders
 2 (
 3 order_id number not null constraint pk$orders primary key using index,
 4 order_date date not null
 5 )
 6 partition by range (order_date) interval (numtoyminterval(1,'month'))
 7 (
 8 partition p2014_01 values less than (to_date('01-feb-2014','DD-MON-YYYY'))
 9 )
 10 ;

Table created.

TVD12@TVD12>
TVD12@TVD12> create table line_items
 2 (
 3 order_id number not null,
 4 pos number not null,
 5 item_id number not null,
 6 quantity number not null,
 7 constraint pk$line_items primary key (order_id, pos) using index,
 8 constraint fk$order_id foreign key (order_id) references orders (order_id)
 9 )
 10 partition by reference (fk$order_id);

Table created.

Let’s insert some data:

TVD12@TVD12> insert into orders
 2 select rownum,to_date('01-JAN-2014','DD-MON-YYYY')+rownum*3
 3 from all_objects where rownum<100;

99 rows created.

TVD12@TVD12> insert into line_items
 2 select order_id,1,99,1
 3 from orders where rownum < 50;

49 rows created.

Which partitions were created?

TVD12@TVD12> select partition_name,high_value from user_tab_partitions
 2 where table_name='ORDERS'
 3 ;

PARTITION_ HIGH_VALUE
---------- ----------------------------------
P2014_01 TO_DATE(' 2014-02-01 00:00:00', ..
SYS_P4268 TO_DATE(' 2014-03-01 00:00:00', ..
SYS_P4269 TO_DATE(' 2014-04-01 00:00:00', ..
SYS_P4270 TO_DATE(' 2014-05-01 00:00:00', ..
SYS_P4271 TO_DATE(' 2014-06-01 00:00:00', ..
SYS_P4272 TO_DATE(' 2014-07-01 00:00:00', ..
SYS_P4273 TO_DATE(' 2014-08-01 00:00:00', ..
SYS_P4274 TO_DATE(' 2014-09-01 00:00:00', ..
SYS_P4275 TO_DATE(' 2014-10-01 00:00:00', ..
SYS_P4276 TO_DATE(' 2014-11-01 00:00:00', ..

10 rows selected.

TVD12@TVD12> select partition_name,high_value,interval from user_tab_partitions
 2 where table_name='LINE_ITEMS'
 3 ;

PARTITION_ HIGH_VALUE INT
---------- ---------- ---
P2014_01 NO
SYS_P4268 YES
SYS_P4269 YES
SYS_P4270 YES
SYS_P4271 YES

Conclusion 1: only the necessary partitions are created (this is a feature for interval partitioned tables, but it’s valid for the child table, too)

Conclusion 2: partition naming is consistent between master and child.

Now we’ll merge two partitions of master table:

TVD12@TVD12> alter table orders merge partitions &p1,&p2;
Enter value for p1: SYS_P4268
Enter value for p2: SYS_P4269
old 1: alter table orders merge partitions &p1,&p2
new 1: alter table orders merge partitions SYS_P4268,SYS_P4269

What happens to the partitions?


TVD12@TVD12> column high_value format a85
TVD12@TVD12> select partition_name,high_value from user_tab_partitions where table_name='ORDERS';

PARTITION_ HIGH_VALUE
---------- -------------------------------------------------------------------------------------
P2014_01 TO_DATE(' 2014-02-01 00:00:00', ..
SYS_P4270 TO_DATE(' 2014-05-01 00:00:00', ..
SYS_P4271 TO_DATE(' 2014-06-01 00:00:00', ..
SYS_P4272 TO_DATE(' 2014-07-01 00:00:00', ..
SYS_P4273 TO_DATE(' 2014-08-01 00:00:00', ..
SYS_P4274 TO_DATE(' 2014-09-01 00:00:00', ..
SYS_P4275 TO_DATE(' 2014-10-01 00:00:00', ..
SYS_P4276 TO_DATE(' 2014-11-01 00:00:00', ..
SYS_P4277 TO_DATE(' 2014-04-01 00:00:00', ..

9 rows selected.

TVD12@TVD12>
TVD12@TVD12> column high_value format a10
TVD12@TVD12> select partition_name,high_value,interval from user_tab_partitions where table_name='LINE_ITEMS';

PARTITION_ HIGH_VALUE INT
---------- ---------- ---
P2014_01 NO
SYS_P4270 YES
SYS_P4271 YES
SYS_P4278 NO

What happens if we try to merge partitions on child level only?

TVD12@TVD12> alter table line_items merge partitions &p3,&p4;
Enter value for p3: SYS_P4270
Enter value for p4: SYS_P4271
old 1: alter table line_items merge partitions &p3,&p4
new 1: alter table line_items merge partitions SYS_P4270,SYS_P4271
alter table line_items merge partitions SYS_P4270,SYS_P4271
 *
ERROR at line 1:
ORA-14650: operation not supported for reference-partitioned tables

Conclusion 3: during merge new partitions are created and partition names (master/child) don’t match anymore (master: SYS_P4277, child: SYS_P4278).

Conclusion 4: the new child partition (SYS_P4278) is marked as “Non-Interval” (column INTERVAL), i.e. it’s a “range partition”

Conclusion 5: partition level on child level only is not possible (ORA-14650)

Let’s take a look at the indexes after the merge:

TVD12@TVD12> column table_name format a15
TVD12@TVD12> column index_name format a30
TVD12@TVD12>
TVD12@TVD12> select table_name,index_name,partitioned,status
 2 from user_indexes where
 3 table_name in ('ORDERS','LINE_ITEMS')
 4 order by table_name,index_name;

TABLE_NAME INDEX_NAME PAR STATUS
--------------- ------------------------------ --- --------
LINE_ITEMS PK$LINE_ITEMS NO UNUSABLE
ORDERS PK$ORDERS NO UNUSABLE

Conclusion 6: partition merge leads to physical row movement. If – like in the example – you do not use “ALTER TABLE . MERGE PARTITIONS .. UPDATE INDEXES” indexes will become invalid and you have to rebuild them.

TVD12@TVD12> alter index pk$orders rebuild;
Index altered.

TVD12@TVD12> alter index pk$line_items rebuild;
Index altered.

Summary: partition merge is possible with interval reference partitionierung and affects both master and child table. You should use “UPDATE INDEXES”.

Let’s continue with question #2: what happens if we update a row in the master table so that it has to be moved into another partition (ROW MOVEMENT)

Initial situation:

TVD12@TVD12> execute dbms_stats.gather_table_stats(ownname=>user,tabname=>'ORDERS');
PL/SQL procedure successfully completed.

TVD12@TVD12> execute dbms_stats.gather_table_stats(ownname=>user,tabname=>'LINE_ITEMS');
PL/SQL procedure successfully completed.

TVD12@TVD12>
TVD12@TVD12> select table_name,partition_name,num_rows from user_tab_partitions where table_name='ORDERS';

TABLE_NAME PARTITION_ NUM_ROWS
--------------- ---------- ----------
ORDERS P2014_01 10
ORDERS SYS_P4270 10
ORDERS SYS_P4271 11
ORDERS SYS_P4272 10
ORDERS SYS_P4273 10
ORDERS SYS_P4274 10
ORDERS SYS_P4275 10
ORDERS SYS_P4276 9
ORDERS SYS_P4277 19
9 rows selected.

TVD12@TVD12> select table_name,partition_name,num_rows from user_tab_partitions where table_name='LINE_ITEMS';

TABLE_NAME PARTITION_ NUM_ROWS
--------------- ---------- ----------
LINE_ITEMS P2014_01 10
LINE_ITEMS SYS_P4270 10
LINE_ITEMS SYS_P4271 10
LINE_ITEMS SYS_P4278 19

TVD12@TVD12> select order_id,order_date from orders where order_date<to_date('01-FEB-2014','DD-MON-YYYY');

ORDER_ID ORDER_DATE
---------- ------------------
 1 04-JAN-14
 2 07-JAN-14
..

Let’s update order #1:

TVD12@TVD12> update orders set order_date=to_date('27-FEB-2014','DD-MON-YYYY') where order_id=1;
update orders set order_date=to_date('27-FEB-2014','DD-MON-YYYY') where order_id=1
 *
ERROR at line 1:
ORA-14402: updating partition key column would cause a partition change

OK, we should enable row movement first.

TVD12@TVD12> alter table orders enable row movement;
alter table orders enable row movement
*
ERROR at line 1:
ORA-14662: row movement cannot be enabled

Conclusion 7: ROW MOVEMENT must be enabled on child level first.

TVD12@TVD12>
TVD12@TVD12> alter table line_items enable row movement;
Table altered.

TVD12@TVD12> alter table orders enable row movement;
Table altered.

TVD12@TVD12>
TVD12@TVD12> pause

TVD12@TVD12>
TVD12@TVD12> update orders set order_date=to_date('27-FEB-2014','DD-MON-YYYY') where order_id=1;
1 row updated.

TVD12@TVD12>
TVD12@TVD12> commit;

Let’s take a look at the tables:

TVD12@TVD12> execute dbms_stats.gather_table_stats(ownname=>user,tabname=>'ORDERS');

PL/SQL procedure successfully completed.

TVD12@TVD12> execute dbms_stats.gather_table_stats(ownname=>user,tabname=>'LINE_ITEMS');

PL/SQL procedure successfully completed.
TVD12@TVD12>
TVD12@TVD12> select table_name,partition_name,num_rows from user_tab_partitions where table_name='ORDERS';

TABLE_NAME PARTITION_ NUM_ROWS
--------------- ---------- ----------
ORDERS P2014_01 9
ORDERS SYS_P4270 10
ORDERS SYS_P4271 11
ORDERS SYS_P4272 10
ORDERS SYS_P4273 10
ORDERS SYS_P4274 10
ORDERS SYS_P4275 10
ORDERS SYS_P4276 9
ORDERS SYS_P4277 20

9 rows selected.

TVD12@TVD12> select table_name,partition_name,num_rows from user_tab_partitions where table_name='LINE_ITEMS';

TABLE_NAME PARTITION_ NUM_ROWS
--------------- ---------- ----------
LINE_ITEMS P2014_01 9
LINE_ITEMS SYS_P4270 10
LINE_ITEMS SYS_P4271 10
LINE_ITEMS SYS_P4278 20

Conclusion 8: records in both master and child table are moved when an update causes row movement in the master table.
Finally, we’ll create a row which has to be stored in new partitions.

TVD12@TVD12> insert into orders (order_id,order_date) values (100,sysdate);
1 row created.

TVD12@TVD12> insert into line_items (order_id,pos,item_id,quantity) values (100,1,999,2);
1 row created.

TVD12@TVD12> commit;
Commit complete.
TVD12@TVD12>
TVD12@TVD12> column high_value format a85
TVD12@TVD12> select partition_name,high_value from user_tab_partitions where table_name='ORDERS';

PARTITION_ HIGH_VALUE
---------- -------------------------------------------------------------------------------------
P2014_01 TO_DATE(' 2014-02-01 00:00:00', ..
SYS_P4270 TO_DATE(' 2014-05-01 00:00:00', ..
SYS_P4271 TO_DATE(' 2014-06-01 00:00:00', ..
SYS_P4272 TO_DATE(' 2014-07-01 00:00:00', ..
SYS_P4273 TO_DATE(' 2014-08-01 00:00:00', ..
SYS_P4274 TO_DATE(' 2014-09-01 00:00:00', ..
SYS_P4275 TO_DATE(' 2014-10-01 00:00:00', ..
SYS_P4276 TO_DATE(' 2014-11-01 00:00:00', ..
SYS_P4277 TO_DATE(' 2014-04-01 00:00:00', ..
SYS_P4279 TO_DATE(' 2017-03-01 00:00:00', ..

10 rows selected.

TVD12@TVD12>
TVD12@TVD12> column high_value format a10
TVD12@TVD12> select partition_name,high_value,interval from user_tab_partitions where table_name='LINE_ITEMS';

PARTITION_ HIGH_VALUE INT
---------- ---------- ---
P2014_01 NO
SYS_P4270 YES
SYS_P4271 YES
SYS_P4278 NO
SYS_P4279 YES

Conclusion 9: when partitions are automatically created by interval partitioning, the names (master and child) are identical.
MOS-Notes:

  • 1519042.1: How to Create Interval-Reference Partitioned Tables in Oracle 12c
    1568010.1: 12c Partitioning Enhancements, New Features
This entry was posted in Common, Oracle 12c. Bookmark the permalink.