表分区机制

本文详细介绍了Oracle数据库中的9种表分区方法,包括区间分区、散列分区、列表分区、间隔分区、引用分区、间隔引用分区、虚拟列分区、系统分区和组合分区。通过示例展示了如何创建和操作这些分区,以及它们在数据管理和查询效率中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

--表分区机制

--Oracle目前有9中对表分区的方法

--区间分区(range partitioning):你可以指定某个分区的数据应该放在一起。比如时间戳一月二月存放在分区一分区二中。
--散列分区(hash partitioning):数据库会在一个或多个列上应用一个散列函数,每行数据都按散列值放在相应的分区中。
--列表分区(list partitioning):指定一个离散值集,来确定应当存储在一起的数据。例如指定STATUS列值在('A','B')中的行放在分区1中,值('C','D')放在分区2中。
--间隔分区(interval partitioning):它与区间分区非常相似,区别在于数据库可以在数据到来时创建新的分区。
--引用分区(reference partitioning):对于一对外键约束的父/子表,如果父表已分区,子表可以通过外键约束来继承父表的分区方式,子表上的这种分区方法称为引用分区。
--间隔引用分区(interval reference partitioning):这种分区就是间隔分区和引用分区的组合。Oracle12c才有这种分区。
--虚拟列分区(virtual column partitioning):这是区间分区、散列分区和列表分区的组合。通过组合分区我们可以先按照某种方式分区(区间、散列、列表),再按照另外某种方式对每个分区的数据划分子分区。
--系统分区(system partitioning):应用来决定将数据写入哪一个分区。这种分区类型比较少见。

--区间分区

EODA@PROD1> CREATE TABLE range_example      --创建一个使用RANGE_KEY_COLUMN分区的区间分区表,严格小于01-JAN-2014的数据放在part_1,严格小于01-JAN-2015的数据放在part_2
  2    ( range_key_column date NOT NULL,
  3  	 data		  varchar2(20)
  4    )
  5    PARTITION BY RANGE (range_key_column)
  6    ( PARTITION part_1 VALUES LESS THAN
  7  	      (to_date('01/01/2014','dd/mm/yyyy')),
  8  	 PARTITION part_2 VALUES LESS THAN
  9  	      (to_date('01/01/2015','dd/mm/yyyy'))
 10    )
 11  /

Table created.

EODA@PROD1> 
EODA@PROD1> insert into range_example   --插入数据
  2  ( range_key_column, data )
  3  values
  4  ( to_date( '15-dec-2013 00:00:00',
  5  		'dd-mon-yyyy hh24:mi:ss' ),
  6    'application data...' );

1 row created.

EODA@PROD1> 
EODA@PROD1> insert into range_example
  2  ( range_key_column, data )
  3  values
  4  ( to_date( '31-dec-2013 23:59:59',
  5  		'dd-mon-yyyy hh24:mi:ss' ),
  6    'application data...' );

1 row created.

EODA@PROD1> 
EODA@PROD1> insert into range_example
  2  ( range_key_column, data )
  3  values
  4  ( to_date( '01-jan-2014 00:00:00',
  5  		'dd-mon-yyyy hh24:mi:ss' ),
  6    'application data...' );

1 row created.

EODA@PROD1> 
EODA@PROD1> insert into range_example
  2  ( range_key_column, data )
  3  values
  4  ( to_date( '31-dec-2014 23:59:59',
  5  		'dd-mon-yyyy hh24:mi:ss' ),
  6    'application data...' );

1 row created.

EODA@PROD1> 
EODA@PROD1> -- should throw an error
EODA@PROD1> insert into range_example   --尝试插入超过分区的值失败
  2  ( range_key_column, data )
  3  values
  4  ( to_date( '01-jan-2015 00:00:00',
  5  		'dd-mon-yyyy hh24:mi:ss' ),
  6    'application data...' );
insert into range_example
            *
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition


EODA@PROD1> 
EODA@PROD1> select to_char(range_key_column,'dd-mon-yyyy hh24:mi:ss')  --查询part_1
  2  from range_example partition (part_1);

TO_CHAR(RANGE_KEY_COLUMN,'DD-
-----------------------------
15-dec-2013 00:00:00
31-dec-2013 23:59:59

EODA@PROD1> 
EODA@PROD1> select to_char(range_key_column,'dd-mon-yyyy hh24:mi:ss')  --查询part_2
  2  from range_example partition (part_2);

TO_CHAR(RANGE_KEY_COLUMN,'DD-
-----------------------------
01-jan-2014 00:00:00
31-dec-2014 23:59:59

EODA@PROD1> 
EODA@PROD1> drop table range_example purge;

Table dropped.

EODA@PROD1> 
EODA@PROD1> CREATE TABLE range_example    --这样可以保证所有数据都能被插入
  2    ( range_key_column date ,
  3  	 data		  varchar2(20)
  4    )
  5    PARTITION BY RANGE (range_key_column)
  6    ( PARTITION part_1 VALUES LESS THAN
  7  	      (to_date('01/01/2014','dd/mm/yyyy')),
  8  	 PARTITION part_2 VALUES LESS THAN
  9  	      (to_date('01/01/2015','dd/mm/yyyy')),
 10  	 PARTITION part_3 VALUES LESS THAN
 11  	      (MAXVALUE)
 12    )
 13  /

Table created.

--散列分区

对于散列分区,Oracle会使用一个散列函数对每一条插入数据的分区列值计算出起散列值,以此确定数据应当放在N个分区中的哪一个分区中。
Oracle建议N是2一个幂,这样表中的数据才能最好的分布在所有的分区上。
表中的散列键值应当各不相同,这样数据才能均匀的分布在各个分区上。

EODA@PROD1> set echo on
EODA@PROD1> 
EODA@PROD1> CREATE TABLE hash_example  --创建两个分区的分区表
  2    ( hash_key_column   date,
  3  	 data		   varchar2(20)
  4    )
  5    PARTITION BY HASH (hash_key_column)
  6    ( partition part_1 tablespace p1,
  7  	 partition part_2 tablespace p2
  8  )
  9  /

Table created.

EODA@PROD1> 
EODA@PROD1> insert into hash_example
  2  ( hash_key_column, data )
  3  values
  4  ( to_date( '25-jun-2014' ),
  5    'application data...' );

1 row created.

EODA@PROD1> 
EODA@PROD1> insert into hash_example
  2  ( hash_key_column, data )
  3  values
  4  ( to_date( '27-feb-2015' ),
  5    'application data...' );

1 row created.

EODA@PROD1> 
EODA@PROD1> select 'part_1', hash_key_column from hash_example partition(part_1) union all
  2  select 'part_2', hash_key_column from hash_example partition(part_2);

'PART_ HASH_KEY_
------ ---------
part_2 25-JUN-14
part_2 27-FEB-15

--散列分区数需要使用2的幂

--首先自动创建一个有N个分区的散列分区表,这个过程会构造一个动态查询,按分区获取其中的行数,再按分区显示行数,最后给出一个简单直方图。
EODA@PROD1> create or replace
  2    procedure hash_proc
  3  		 ( p_nhash in number,
  4  		   p_cursor out sys_refcursor )
  5    authid current_user
  6    as
  7  	   l_text     long;
  8  	   l_template long :=
  9  		  'select $POS$ oc, ''p$POS$'' pname, count(*) cnt ' ||
 10  		    'from t partition ( $PNAME$ ) union all ';
 11  	   table_or_view_does_not_exist exception;
 12  	   pragma exception_init( table_or_view_does_not_exist, -942 );
 13  begin
 14  	   begin
 15  	       execute immediate 'drop table t';
 16  	   exception when table_or_view_does_not_exist
 17  	       then null;
 18  	   end;
 19  
 20  	   execute immediate '
 21  	   CREATE TABLE t ( id )
 22  	   partition by hash(id)
 23  	   partitions ' || p_nhash || '
 24  	   as
 25  	   select rownum
 26  	    from all_objects';
 27  
 28  	   for x in ( select partition_name pname,
 29  			     PARTITION_POSITION pos
 30  			from user_tab_partitions
 31  		       where table_name = 'T'
 32  		       order by partition_position )
 33  	   loop
 34  	       l_text := l_text ||
 35  			 replace(
 36  			 replace(l_template,
 37  			       '$POS$', x.pos),
 38  			       '$PNAME$', x.pname );
 39  	   end loop;
 40  
 41  	   open p_cursor for
 42  	      'select pname, cnt,
 43  		 substr( rpad(''*'',30*round( cnt/max(cnt)over(),2),''*''),1,30) hg
 44  		 from (' || substr( l_text, 1, length(l_text)-11 ) || ')
 45  		 order by oc';
 46  
 47  end;
 48  /

Procedure created.

EODA@PROD1> 
EODA@PROD1> set lines 132
EODA@PROD1> column hg format a50
EODA@PROD1> variable x refcursor
EODA@PROD1> set autoprint on
EODA@PROD1> exec hash_proc( 4, :x );  --数据均匀分布在了4个分区中

PL/SQL procedure successfully completed.


PN	  CNT HG
-- ---------- --------------------------------------------------
p1	18208 *****************************
p2	18183 *****************************
p3	18415 ******************************
p4	18096 *****************************

EODA@PROD1> exec hash_proc( 5, :x );  --第一个和最后一个分区是中间三个的一半,数据没有均匀分区

PL/SQL procedure successfully completed.


PN	  CNT HG
-- ---------- --------------------------------------------------
p1	 9178 ***************
p2	18185 *****************************
p3	18416 ******************************
p4	18096 *****************************
p5	 9032 **************

EODA@PROD1> exec hash_proc( 6, :x );  --同五个分区的趋势一样

PL/SQL procedure successfully completed.


PN	  CNT HG
-- ---------- --------------------------------------------------
p1	 9179 ***************
p2	 9229 ***************
p3	18417 ******************************
p4	18098 *****************************
p5	 9032 **************
p6	 8958 **************

6 rows selected.

EODA@PROD1> exec hash_proc( 7, :x );	--同五个分区的趋势一样

PL/SQL procedure successfully completed. 


PN	  CNT HG
-- ---------- --------------------------------------------------
p1	 9179 ***************
p2	 9231 ***************
p3	 9205 ***************
p4	18099 ******************************
p5	 9035 ***************
p6	 8958 **************
p7	 9213 ***************

7 rows selected.

EODA@PROD1> exec hash_proc( 8, :x );  --数据分布又变为均匀

PL/SQL procedure successfully completed.


PN	  CNT HG
-- ---------- --------------------------------------------------
p1	 9181 *****************************
p2	 9231 ******************************
p3	 9206 ******************************
p4	 9068 *****************************
p5	 9036 *****************************
p6	 8959 *****************************
p7	 9213 ******************************
p8	 9034 *****************************

8 rows selected.

--很明显得出结论!散列分区数要使用的2的幂

--列表分区

列表分区是Oracle9i的一个新特性,它为每个分区指定了一个离散值列表,每行数据根据其分区键值所属列表归到相应的分区中。

EODA@PROD1> create table list_example
  2    ( state_cd   varchar2(2),
  3  	 data	    varchar2(20)
  4    )
  5  partition by list(state_cd)
  6  ( partition part_1 values ( 'ME', 'NH', 'VT', 'MA' ), --列表分区
  7    partition part_2 values ( 'CT', 'RI', 'NY' )
  8  )
  9  /

Table created.

EODA@PROD1> 
EODA@PROD1> insert into list_example (state_cd, data)
  2  values ('CT', 'application data...');

1 row created.

EODA@PROD1> insert into list_example (state_cd, data)
  2  values ('MA', 'application data...');

1 row created.

EODA@PROD1> insert into list_example (state_cd, data)
  2  values ('ME', 'application data...');

1 row created.

EODA@PROD1> insert into list_example (state_cd, data)
  2  values ('NH', 'application data...');

1 row created.

EODA@PROD1> insert into list_example (state_cd, data)
  2  values ('NY', 'application data...');

1 row created.

EODA@PROD1> insert into list_example (state_cd, data)
  2  values ('RI', 'application data...');

1 row created.

EODA@PROD1> insert into list_example (state_cd, data)
  2  values ('VT', 'application data...');

1 row created.

EODA@PROD1> select * from list_example partition (part_1);

ST DATA
-- --------------------
MA application data...
ME application data...
NH application data...
VT application data...

EODA@PROD1> 
EODA@PROD1> insert into list_example values ( 'VA', 'data' );  --不存在插入失败
insert into list_example values ( 'VA', 'data' )
            *
ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition


EODA@PROD1> 
EODA@PROD1> alter table list_example  --增加默认分区
  2    add partition
  3    part_3 values ( DEFAULT );

Table altered.

EODA@PROD1> 
EODA@PROD1> insert into list_example values ( 'VA', 'data' );  --插入成功
 
1 row created.


--间隔分区

间隔分区就是以一个区间分区表为起点,不过他在定义中还有一个规则,让数据库知道如何增加分区。

EODA@PROD1> create table audit_trail	--设置分区间隔为每个月一个分区,p0设置为过度分区,所有严格小于这个过度分区上限的数据都会被放到这个分区中。
  2    ( ts	  timestamp,
  3  	 data  varchar2(30)
  4    )
  5    partition by range(ts)
  6    interval (numtoyminterval(1,'month'))
  7    store in (users, example)   --一般分配多个表空间
  8    (partition p0 values less than
  9    (to_date('01-01-1900','dd-mm-yyyy'))
 10  )
 11  /

Table created.

EODA@PROD1> column partition_name  format a10
EODA@PROD1> column tablespace_name format a10
EODA@PROD1> column high_value	   format a31
EODA@PROD1> column interval	   format a30
EODA@PROD1> 
EODA@PROD1> select a.partition_name, a.tablespace_name, a.high_value,   --查看现有分区
  2    decode( a.interval, 'YES', b.interval ) interval
  3  from user_tab_partitions a, user_part_tables b
  4  where a.table_name = 'AUDIT_TRAIL'
  5  and a.table_name = b.table_name
  6  order by a.partition_position;

PARTITION_ TABLESPACE HIGH_VALUE		      INTERVAL
---------- ---------- ------------------------------- ------------------------------
P0	   USERS      TIMESTAMP' 1900-01-01 00:00:00'

EODA@PROD1> 
EODA@PROD1> insert into audit_trail (ts,data) values
  2  ( to_timestamp('27-feb-2014','dd-mon-yyyy'), 'xx' );  --插入数据

1 row created.

EODA@PROD1> 
EODA@PROD1> select a.partition_name, a.tablespace_name, a.high_value,  --观察到自动创建了SYS_P71分区
  2    decode( a.interval, 'YES', b.interval ) interval
  3  from user_tab_partitions a, user_part_tables b
  4  where a.table_name = 'AUDIT_TRAIL'
  5  and a.table_name = b.table_name
  6  order by a.partition_position;

PARTITION_ TABLESPACE HIGH_VALUE		      INTERVAL
---------- ---------- ------------------------------- ------------------------------
P0	   USERS      TIMESTAMP' 1900-01-01 00:00:00'
SYS_P71    USERS      TIMESTAMP' 2014-03-01 00:00:00' NUMTOYMINTERVAL(1,'MONTH')

EODA@PROD1> 
EODA@PROD1> column greater_than_eq_to format a32
EODA@PROD1> column strictly_less_than format a32
EODA@PROD1> 
EODA@PROD1> select
  2  TIMESTAMP' 2014-03-01 00:00:00'-NUMTOYMINTERVAL(1,'MONTH') greater_than_eq_to,
  3  TIMESTAMP' 2014-03-01 00:00:00' strictly_less_than
  4  from dual
  5  /

GREATER_THAN_EQ_TO		 STRICTLY_LESS_THAN
-------------------------------- --------------------------------
01-FEB-14 12.00.00.000000000 AM  01-MAR-14 12.00.00.000000000 AM

EODA@PROD1> 
EODA@PROD1> insert into audit_trail (ts,data) values
  2  ( to_date('25-jun-2014','dd-mon-yyyy'), 'xx' );

1 row created.

EODA@PROD1> 
EODA@PROD1> select a.partition_name, a.tablespace_name, a.high_value,  --发现插入两行都存在了USERS表空间内
  2    decode( a.interval, 'YES', b.interval ) interval
  3  from user_tab_partitions a, user_part_tables b
  4  where a.table_name = 'AUDIT_TRAIL'
  5  and a.table_name = b.table_name
  6  order by a.partition_position;

PARTITION_ TABLESPACE HIGH_VALUE		      INTERVAL
---------- ---------- ------------------------------- ------------------------------
P0	   USERS      TIMESTAMP' 1900-01-01 00:00:00'
SYS_P71    USERS      TIMESTAMP' 2014-03-01 00:00:00' NUMTOYMINTERVAL(1,'MONTH')
SYS_P72    USERS      TIMESTAMP' 2014-07-01 00:00:00' NUMTOYMINTERVAL(1,'MONTH')

EODA@PROD1> 
EODA@PROD1> insert into audit_trail (ts,data) values  --插入奇数月份
  2  ( to_date('15-mar-2014','dd-mon-yyyy'), 'xx' );

1 row created.

EODA@PROD1> 
EODA@PROD1> select a.partition_name, a.tablespace_name, a.high_value,  --发现成功创建在另一个表空间中,说明通过奇偶数来存放表空间
  2  	      decode( a.interval, 'YES', b.interval ) interval
  3  from user_tab_partitions a, user_part_tables b
  4  where a.table_name = 'AUDIT_TRAIL'
  5  and a.table_name = b.table_name
  6  order by a.partition_position;

PARTITION_ TABLESPACE HIGH_VALUE		      INTERVAL
---------- ---------- ------------------------------- ------------------------------
P0	   USERS      TIMESTAMP' 1900-01-01 00:00:00'
SYS_P71    USERS      TIMESTAMP' 2014-03-01 00:00:00' NUMTOYMINTERVAL(1,'MONTH')
SYS_P73    EXAMPLE    TIMESTAMP' 2014-04-01 00:00:00' NUMTOYMINTERVAL(1,'MONTH')
SYS_P72    USERS      TIMESTAMP' 2014-07-01 00:00:00' NUMTOYMINTERVAL(1,'MONTH')

EODA@PROD1>  --更改系统自动创建的表空间名字
EODA@PROD1> declare
  2  	   l_str varchar2(4000);
  3  begin
  4  	  for x in ( select a.partition_name, a.tablespace_name, a.high_value
  5  		       from user_tab_partitions a
  6  		       where a.table_name = 'AUDIT_TRAIL'
  7  		       and a.interval = 'YES'
  8  		       and a.partition_name like 'SYS\_P%' escape '\' )
  9  	  loop
 10  	       execute immediate
 11  	      'select to_char( ' || x.high_value ||
 12  		  '-numtodsinterval(1,''second''), ''"PART_"yyyy_mm'' ) from dual'
 13  		 into l_str;
 14  	       execute immediate
 15  	       'alter table audit_trail rename partition "' ||
 16  		   x.partition_name || '" to "' || l_str || '"';
 17  	  end loop;
 18  end;
 19  /

PL/SQL procedure successfully completed.

EODA@PROD1> 
EODA@PROD1> set lines 132
EODA@PROD1> column partition_name format a20
EODA@PROD1> 
EODA@PROD1> select a.partition_name, a.tablespace_name, a.high_value,
  2    decode( a.interval, 'YES', b.interval ) interval
  3  from user_tab_partitions a, user_part_tables b
  4  where a.table_name = 'AUDIT_TRAIL'
  5  and a.table_name = b.table_name
  6  order by a.partition_position;

PARTITION_NAME	     TABLESPACE HIGH_VALUE			INTERVAL
-------------------- ---------- ------------------------------- ------------------------------
P0		     USERS	TIMESTAMP' 1900-01-01 00:00:00'
PART_2014_02	     USERS	TIMESTAMP' 2014-03-01 00:00:00' NUMTOYMINTERVAL(1,'MONTH')
PART_2014_03	     EXAMPLE	TIMESTAMP' 2014-04-01 00:00:00' NUMTOYMINTERVAL(1,'MONTH')
PART_2014_06	     USERS	TIMESTAMP' 2014-07-01 00:00:00' NUMTOYMINTERVAL(1,'MONTH')


--引用分区

引用分区是Oracle 11g R1以上版本的一个新特性,用于处理父/子对等分区的问题。
也就是说,我们可以某种方式对子表分区,使得子表的各个分区与父表相应的分区是一对一关系。

EODA@PROD1> create table orders  --创建分区父表
  2    (
  3  	 order#      number primary key,
  4  	 order_date  date,
  5  	 data	    varchar2(30)
  6    )
  7  enable row movement
  8  PARTITION BY RANGE (order_date)
  9    (
 10  	 PARTITION part_2014 VALUES LESS THAN (to_date('01-01-2015','dd-mm-yyyy')) ,
 11  	 PARTITION part_2015 VALUES LESS THAN (to_date('01-01-2016','dd-mm-yyyy'))
 12  )
 13  /

Table created.

EODA@PROD1> 
EODA@PROD1> insert into orders values
  2    ( 1, to_date( '01-jun-2014', 'dd-mon-yyyy' ), 'xxx' );

1 row created.

EODA@PROD1> 
EODA@PROD1> insert into orders values
  2    ( 2, to_date( '01-jun-2015', 'dd-mon-yyyy' ), 'xxx' );

1 row created.

EODA@PROD1> 
EODA@PROD1> create table order_line_items  --创建子表
  2    (
  3  	 order#      number,
  4  	 line#	     number,
  5  	 data	    varchar2(30),
  6  	 constraint c1_pk primary key(order#,line#),
  7  	 constraint c1_fk_p foreign key(order#) references orders
  8    )
  9  enable row movement
 10  partition by reference(c1_fk_p)
 11  /

Table created.

EODA@PROD1> 
EODA@PROD1> insert into order_line_items values ( 1, 1, 'yyy' );

1 row created.

EODA@PROD1> 
EODA@PROD1> insert into order_line_items values ( 2, 1, 'yyy' );

1 row created.

EODA@PROD1> 
EODA@PROD1> column table_name format a20
EODA@PROD1> column partition_name format a20
EODA@PROD1> 
EODA@PROD1> select table_name, partition_name  --子表自动获得分区
  2  from user_tab_partitions
  3  where table_name in ( 'ORDERS', 'ORDER_LINE_ITEMS' )
  4  order by table_name, partition_name
  5  /

TABLE_NAME	     PARTITION_NAME
-------------------- --------------------
ORDERS		     PART_2014
ORDERS		     PART_2015
ORDER_LINE_ITEMS     PART_2014
ORDER_LINE_ITEMS     PART_2015

EODA@PROD1> 
EODA@PROD1> alter table orders drop partition part_2014 update global indexes;  --删除父表分区

Table altered.

EODA@PROD1> 
EODA@PROD1> select table_name, partition_name     --子表自动删除分区
  2  from user_tab_partitions
  3  where table_name in ( 'ORDERS', 'ORDER_LINE_ITEMS' )
  4  order by table_name, partition_name
  5  /

TABLE_NAME	     PARTITION_NAME
-------------------- --------------------
ORDERS		     PART_2015
ORDER_LINE_ITEMS     PART_2015

EODA@PROD1> 
EODA@PROD1> alter table orders add partition
  2   part_2016 values less than
  3   (to_date( '01-01-2017', 'dd-mm-yyyy' ));

Table altered.

EODA@PROD1> 
EODA@PROD1> select table_name, partition_name
  2  from user_tab_partitions
  3  where table_name in ( 'ORDERS', 'ORDER_LINE_ITEMS' )
  4  order by table_name, partition_name
  5  /

TABLE_NAME	     PARTITION_NAME
-------------------- --------------------
ORDERS		     PART_2015
ORDERS		     PART_2016
ORDER_LINE_ITEMS     PART_2015
ORDER_LINE_ITEMS     PART_2016

EODA@PROD1> 
EODA@PROD1> select '2015', count(*) from order_line_items partition(part_2015)
  2  union all
  3  select '2016', count(*) from order_line_items partition(part_2016);

'201   COUNT(*)
---- ----------
2015	      1
2016	      0

EODA@PROD1> 
EODA@PROD1> update orders set order_date = add_months(order_date,12);  --对父表更新

1 row updated.

EODA@PROD1> 
EODA@PROD1> select '2015', count(*) from order_line_items partition(part_2015)
  2  union all
  3  select '2016', count(*) from order_line_items partition(part_2016);

'201   COUNT(*)
---- ----------
2015	      0 
2016	      1        --子表中的数据也异动了,这只有在父表定义为允许行移动才有效。

--间隔引用分区 
--只有版本12c以后才有效

SQL> select banner from v$version;  -12c

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


SQL> create table orders  --创建间隔分区的父表
  2    (order#	    number primary key,
  3  	order_date  timestamp,
  4  	data	    varchar2(30))
  5  PARTITION BY RANGE (order_date)
  6  INTERVAL (numtoyminterval(1,'year'))
  7    (PARTITION part_2014 VALUES LESS THAN (to_date('01-01-2015','dd-mm-yyyy')) ,
  8  	PARTITION part_2015 VALUES LESS THAN (to_date('01-01-2016','dd-mm-yyyy')));

Table created.

SQL> 
SQL> create table order_line_items  --创建引用分区的子表
  2  ( order#	   number,
  3    line#	   number,
  4    data	   varchar2(30),
  5    constraint c1_pk primary key(order#,line#),
  6    constraint c1_fk_p foreign key(order#) references orders)
  7  partition by reference(c1_fk_p);

Table created.

SQL> 
SQL> insert into orders values (1, to_date('01-jun-2014', 'dd-mon-yyyy'), 'xxx');

1 row created.

SQL> insert into orders values (2, to_date('01-jun-2015', 'dd-mon-yyyy'), 'xxx');

1 row created.

SQL> insert into order_line_items values (1, 1, 'yyy');

1 row created.

SQL> insert into order_line_items values (2, 1, 'yyy');

1 row created.

SQL> 
SQL> column table_name format a25
SQL> column partition_name format a20
SQL> 
SQL> select table_name, partition_name from user_tab_partitions  --查询已有分区
  2  where table_name in ( 'ORDERS', 'ORDER_LINE_ITEMS' )
  3  order by table_name, partition_name;

TABLE_NAME		  PARTITION_NAME
------------------------- --------------------
ORDERS			  PART_2014
ORDERS			  PART_2015
ORDER_LINE_ITEMS	  PART_2014
ORDER_LINE_ITEMS	  PART_2015

SQL> 
SQL> insert into orders values (3, to_date( '01-jun-2016', 'dd-mon-yyyy' ), 'xxx');  --再插入不能插入已有分区的数据

1 row created.

SQL> insert into order_line_items values (3, 1, 'yyy');

1 row created.

SQL> 
SQL> column partition_name format a10
SQL> column table_name format a16
SQL> column interval format a25
SQL> column high_value format a31
SQL> 
SQL> select a.table_name, a.partition_name, a.high_value,   --oracle自动创建新的分区成功
  2  decode( a.interval, 'YES', b.interval ) interval
  3  from user_tab_partitions a, user_part_tables b
  4  where a.table_name IN ('ORDERS', 'ORDER_LINE_ITEMS')
  5  and a.table_name = b.table_name
  6  order by a.table_name;

TABLE_NAME	 PARTITION_ HIGH_VALUE
---------------- ---------- -------------------------------
INTERVAL
-------------------------
ORDERS		 PART_2014  TIMESTAMP' 2015-01-01 00:00:00'


ORDERS		 PART_2015  TIMESTAMP' 2016-01-01 00:00:00'


ORDERS		 SYS_P1133  TIMESTAMP' 2017-01-01 00:00:00'
NUMTOYMINTERVAL(1,'YEAR')


TABLE_NAME	 PARTITION_ HIGH_VALUE
---------------- ---------- -------------------------------
INTERVAL
-------------------------
ORDER_LINE_ITEMS PART_2014


ORDER_LINE_ITEMS PART_2015


ORDER_LINE_ITEMS SYS_P1133
YES


6 rows selected.

--虚拟列分区 

我们可以通过虚拟列分区来基于某个SQL表达式对数据进行分区,当表中某列充斥着大量的业务值,并且我们只想以这列的某一部分来分区数据时,就可以使用。

EODA@PROD1> create table res(
  2    reservation_code varchar2(30),
  3    region as (substr(reservation_code,1,1))  --虚拟列
  4    )
  5    partition by list (region)
  6  (partition p1 values('A'),
  7   partition p2 values('B'),
  8   partition p3 values('C'),
  9   partition p4 values('D'));

Table created.

EODA@PROD1> 
EODA@PROD1> create index r1 on res(reservation_code) local;

Index created.

EODA@PROD1> 
EODA@PROD1> insert into res (reservation_code)
  2  select chr(64+(round(dbms_random.value(1,4)))) || level
  3  from dual connect by level < 100000;

99999 rows created.

EODA@PROD1> 
EODA@PROD1> exec dbms_stats.gather_table_stats(user,'RES');

PL/SQL procedure successfully completed.

EODA@PROD1> 
EODA@PROD1> explain plan for select count(*) from res where reservation_code='A';

Explained.

EODA@PROD1> select * from table(dbms_xplan.display(null, null, 'BASIC +PARTITION'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 3813016412

-------------------------------------------------------
| Id  | Operation	       | Name | Pstart| Pstop |
-------------------------------------------------------
|   0 | SELECT STATEMENT       |      |       |       |
|   1 |  SORT AGGREGATE        |      |       |       |
|   2 |   PARTITION LIST SINGLE|      |     1 |     1 |   --只扫描了一个分区
|   3 |    INDEX RANGE SCAN    | R1   |     1 |     1 |
-------------------------------------------------------

10 rows selected.

--组合分区
组合分区就是区间分区、散列分区以及列表分区的组合。
CREATE TABLE composite_range_list_example
  ( range_key_column   date,
    code_key_column    int,
    data               varchar2(20)
  )
  PARTITION BY RANGE (range_key_column)
  subpartition by list(code_key_column)
  (
  PARTITION part_1
       VALUES LESS THAN(to_date('01/01/2014','dd/mm/yyyy'))
       (subpartition part_1_sub_1 values( 1, 3, 5, 7 ),
        subpartition part_1_sub_2 values( 2, 4, 6, 8 )
       ),
  PARTITION part_2
      VALUES LESS THAN(to_date('01/01/2015','dd/mm/yyyy'))
      (subpartition part_2_sub_1 values ( 1, 3 ),
       subpartition part_2_sub_2 values ( 5, 7 ),
       subpartition part_2_sub_3 values ( 2, 4, 6, 8 )
      )
)
/
--参考来源《Oracle编程艺术深入理解 数据库 体系结构(第三版)》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值