引用分区(reference partitioning)是Oracle Database 11g Release 1及以上版本的一个新特性。它处理的是父/子对等分区的问题。也就是说,要以某种方式对子表分区,使得各个子表分区分别与一个你表分区存在一对一的关系。在某些情况下这很重要,例如假设有一个数据仓库,你希望保证一定数量的数据在线(例如最近5年的ORDER信息),而且要确保相关联的子表数据(ORDER_LINE_ITEMS数据)也在线。在这个经典的例子中,ORDERS表通常有一个ORDER_DATE列,所以可以很容易地按月分区,这也有利于保证最近5年的数据在线。随着时间推移,只需加载下一个朋的分区,并删除最老的分区。不过,考虑ORDER_LINE_ITEMS表时会看到存在一个问题。它没有ORDER_DATE列,而且ORDER_LINE_ITEMS表中根本没法有可以据以分区的列,因此无法帮助清除老信息或加载新信息。
过去,在引用分区出现之前,开发人员必须对数据逆规范化(denormalize),具体做法是:从父表ORDERS将ORDER_DATE属性复制到子表ORDER_LINE_ITEMS。这会引入冗余数据,相应地带来数据冗余存在的一系列常见问题,比如存储开销增加、数据加载资源增加、级联更新问题(如果修改父表,还必须确保更新父表数据的所有副本),等等。另外,如果在数据库中启用了外键约束(而且确实应当启用外键约束),会发现无法截除或删除父表中原来的分区。例如,下面来创建传统的ORDERS和ORDER_LINE_ITEMS表。先看ORDERS表:
zx@ORCL>create table orders
2 (
3 order# number primary key,
4 order_date date NOT NULL,
5 data varchar2(30)
6 )
7 enable row movement
8 PARTITION BY RANGE (order_date)
9 (
10 PARTITION part_2016 VALUES LESS THAN (to_date('01-01-2017','dd-mm-yyyy')) ,
11 PARTITION part_2017 VALUES LESS THAN (to_date('01-01-2018','dd-mm-yyyy'))
12 )
13 /
Table created.
zx@ORCL>insert into orders values
2 ( 1, to_date( '01-jun-2016', 'dd-mon-yyyy' ), 'xxx' );
1 row created.
zx@ORCL>insert into orders values
2 ( 2, to_date( '01-jun-2017', 'dd-mon-yyyy' ), 'xxx' );
1 row created.
zx@ORCL>commit;
Commit complete.
现在来创建ORDER_LINE_ITEMS表,并插入一些数据指向ORDERS表:
zx@ORCL>create table order_line_items
2 (
3 order# number,
4 line# number,
5 order_date date, -- manually copied from ORDERS!
6 data varchar2(30),
7 constraint c1_pk primary key(order#,line#),
8 constraint c1_fk_p foreign key(order#) references orders
9 )
10 enable row movement
11 PARTITION BY RANGE (order_date)
12 (
13 PARTITION part_2016 VALUES LESS THAN (to_date('01-01-2017','dd-mm-yyyy')) ,
14 PARTITION part_2017 VALUES LESS THAN (to_date('01-01-2018','dd-mm-yyyy'))
15 )
16 /
Table created.
zx@ORCL>insert into order_line_items values
2 ( 1, 1, to_date( '01-jun-2016', 'dd-mon-yyyy' ), 'yyy' );
1 row created.
zx@ORCL>insert into order_line_items values
2 ( 2, 1, to_date( '01-jun-2017', 'dd-mon-yyyy' ), 'yyy' );
1 row created.
zx@ORCL>commit;
Commit complete.
现在如果要删除包含2016年数据的ORDER_LINE_ITEMS分区,也可以删除对应2016年的ORDERS分区而不会违反引用完整性约束。尽管我们都很清楚这一点,但数据库并不知道:
zx@ORCL>alter table order_line_items drop partition part_2016;
Table altered.
zx@ORCL>alter table orders drop partition part_2016;
alter table orders drop partition part_2016
*
ERROR at line 1:
ORA-02266: unique/primary keys in table referenced by enabled foreign keys
所以,对数据逆规范化的做活很笨拙,会耗费资源,而且可能破坏数据的完整性。不仅如此,它还会妨碍管理分区表时经常需要做的一项工作:清除老信息。
下面来看引用分区。采用引用分区,子表会继承父表的分区机制,而不必对分区键逆规范化,而且更重要的是,它会让数据库了解这个子表与父表之间存在对等分区特点。也就是说,截除或删除生意人子表分区时,也能删除或截除父表分区。
要重新实现前面的例子,语法很简单,如下所示,这里将重用现胡的你表ORDERS,只需要截除这个表:
zx@ORCL>drop table order_line_items cascade constraints;
Table dropped.
zx@ORCL>truncate table orders;
Table truncated.
zx@ORCL>insert into orders values
2 ( 1, to_date( '01-jun-2016', 'dd-mon-yyyy' ), 'xxx' );
1 row created.
zx@ORCL>insert into orders values
2 ( 2, to_date( '01-jun-2017', 'dd-mon-yyyy' ), 'xxx' );
1 row created.
zx@ORCL>commit;
Commit complete.