Oracle是最早支持物理分区的数据库管理系统供应商,表分区的功能是在Oracle 8.0版本推出的。分区功能能够改善应用程序的性能、可管理性和可用性,是数据库管理中一个非常关键的技术。尤其在今天,数据库应用系统的规模越来越大,还有海量数据的数据仓储系统,因此,几乎所有的Oracle数据库都使用分区功能来提高查询的性能,并且简化数据库的日常管理维护工作。
那么使用分区技术有哪些优点呢?具体如下:
Oracle 11g相对于其它低级版本的Oracle在分区方面增加了很多功能,具体如下:
创建范围分区的关键字是“RANGE”,创建该分区后,其中的数据可以根据分区键值指定的范围进行分布,当数据在范围内均匀分布时,性能最好。例如,如果选择一个日期列作为分区键,分区“AUG-2016”就会包括所有从01-AUG-2016到31-AUG-2016之间的分区键值(假设分区的范围是从该月的第一天到该月的最后一天)。
当表结构采用范围分区时,首先要考虑分区的列应该符合范围分区的方法;其次要考虑列的数据值的取值范围;最后考虑列的边界问题,下面通过若干具体实例来演示范围分区的创建。
----创建一个商品零售表,然后为该表按照销售日期所在的季度创建4个分区
SQL> create table ware_retail_part --创建一个描述商品零售的数据表
2 (
3 id integer primary key, --销售编号
4 retail_date date, --销售日期
5 ware_name varchar2(50) --商品名称
6 )
7 partition by range(retail_date)
8 (
9 --2016年第一个季度为par_01分区
10 partition par_01 values less than(to_date('2016-04-01','yyyy-mm-dd')) tablespace TBSP_1,
11 --2016年第二个季度为par_02分区
12 partition par_02 values less than(to_date('2016-07-01','yyyy-mm-dd')) tablespace TBSP_1,
13 --2016年第三个季度为par_03分区
14 partition par_03 values less than(to_date('2016-10-01','yyyy-mm-dd')) tablespace TBSP_2,
15 --2016年第四个季度为par_04分区
16 partition par_04 values less than(to_date('2017-01-01','yyyy-mm-dd')) tablespace TBSP_2
17 );
表已创建。
在为商品零售表ware_retail_part创建了4个范围分区之后,下面向该表中插入若干条记录
----向表ware_retail_part插入3条记录,代码及运行结果如下。
SQL> insert into ware_retail_part values(1,to_date('2011-01-20','yyyy-mm-dd'),' 平板电脑');
已创建 1 行。
SQL> insert into ware_retail_part values(2,to_date('2011-04-15','yyyy-mm-dd'),'s 智能手机');
已创建 1 行。
SQL> insert into ware_retail_part values(3,to_date('2011-07-25','yyyy-mm-dd'),'s MP5');
已创建 1 行。
在向ware_retail_part表中插入若干条记录之后,用户就可以通过分区表(即进行了分区的数据表)来查询数据了,这种方式的查询速度要比从整个表中查询快得多,使用分区表查看数据的例子如下。
-----查询数据表ware_retail_part中分区par_02中的全部记录
SQL> select * from ware_retail_part partition(par_02);
另外, Range分区的字段可以是两个或者多个,来看下面的例子。
----创建一个商品零售表,然后为该表按照销售编号和销售日期的组合创建3个分区
SQL> create table ware_retail_part2 --创建一个描述商品零售的数据表
2 (
3 id integer primary key, --销售编号
4 retail_date date, --销售日期
5 ware_name varchar2(50) --商品名称
6 )
7 partition by range(id,retail_date) --按照销售序号和销售日期分区
8 (
9 --第一个分区par_01
10 partition par_01 values less than(10000,to_date('2011-12-01','yyyy-mm-dd')) tablespace TBSP_1,
11 --第一个分区par_02
12 partition par_02 values less than(20000,to_date('2012-12-01','yyyy-mm-dd')) tablespace TBSP_1,
13 --第一个分区par_03
14 partition par_03 values less than(maxvalue,maxvalue) tablespace TBSP_2 15 );
表已创建。
在上面的例子中,partition by range(id,retail_date)作为分区方法,id和retail_date作为分区键,即按销售编号和销售日期的组合来进行区分;语句“partition par_01 values less than(10000,to_date(‘2011-12-01’,’yyyy-mm-dd’)) tablespace TBSP_1”表示一个分区的定义,当插入记录的销售日期小于2011年12月1日,并且销售编号小于10000时,则将该记录划为分区par_01并存放在TBSP_1表空间上。
HASH分区,也叫做散列分区,是在列的取值难以确定的情况下采用的分区方法。比如,按照身份证号进行分区,就很难确定身份证号的分区范围。HASH实际上是一种函数算法,当向表中插入数据时,系统会自动根据当前分区列的值计算出HASH值,然后确定应该将该行存放于哪个表空间中。
HASH分区通过指定分区编号将数据均匀分布在磁盘设备上,使得这些分区大小一致,这充分降低了I/O磁盘争用的情况,但是,对于范围查询或不等式查询起不到优化的作用。
一般,下面几种情况可以采用HASH分区。
下面通过几个示例来演示如何创建散列分区。
------创建一个商品零售表,然后将该表id列的值根据自身情况散列地存放在指定的两个表空间中
SQL> create table ware_retail_part3 --创建一个描述商品零售的数据表
2 (
3 id integer primary key, --销售编号
4 retail_date date, --销售日期
5 ware_name varchar2(50) --商品名称
6 )
7 partition by hash(id)
8 (
9 partition par_01 tablespace TBSP_1, --创建par_01分区
10 partition par_02 tablespace TBSP_2 --创建par_02分区
11 );
表已创建。
关键字是LIST,如果表的某个列的值可以枚举,则可以考虑对表进行列表分区。比如客户表clients,那么就可以按照客户所在的省份进行分区,该表的列表分区可以分为partition shandong(山东省)、partition guangdong(广东省)与partiton yunnan(云南省)等,下面来看一个例子。
----首先创建一个用于保存客户信息的表clients,然后以province列为分区键创建列表分区
SQL> create table clients --创建客户表
2 (
3 id integer primary key, --客户编号
4 name varchar2(50), --客户名称
5 province varchar2(20) --客户所在省份
6 )
7 partition by list(province) --以province列为分区键创建列表分区
8 (
9 partition shandong values('山东省'), --山东省份区
10 partition guangdong values('广东省'), --广东省份区
11 partition yunnan values('云南省') --云南省份区
12 );
表已创建。
在为客户表clients创建了列表分区之后,接下来下面向该表中插入一条记录
------向表clients插入一条记录,省份字段的值为就“云南省”,代码及运行结果如下。
SQL> insert into clients values(19,'东方','云南省');
已创建 1 行。
由于上面插入记录的province字段的值为“云南省”,所以该记录被存储到名称为“yunnan”的表分区中,下面通过SELECT语句来查询“yunnan”这个表分区中的记录,查询结果如图
结合两个数据分区的方法可以成为一个组合分区方法。首先用第一个数据分布方法对表格进行分区,然后再用第二个数据分区方法对每个分区进行二次分区。
Oracle 11g支持以下的组合分区方案。
注意:目前的Oracle仅支持对索引组织表(索引和数据一起的表格)进行范围分区、列表分区或散列分区,但不支持对其进行组合分区。
------首先创建一个保存人员信息的数据表person2,然后创建3个范围分区,每个分区又包含2个子分区,子分区没有名字,由系统自动生成,并要求将其分布在2个指定的表空间中
SQL> create table person2 --创建以一个描述个人信息的表
2 (
3 id number primary key, --个人的编号
4 name varchar2(20), --姓名
5 sex varchar2(2) --性别
6 )
7 partition by range(id) --以id作为分区键创建范围分区
8 subpartition by hash(name) --以name列作为分区键创建hash子分区
9 subpartitions 2 store in(tbsp_1,tbsp_2) --hash子分区公有两个,分别存储在两个不同的命名空间中
10 (
11 partition par1 values less than(5000), --范围分区,id小于5000
12 partition par2 values less than(10000), --范围分区,id小于10000
13 partition par3 values less than(maxvalue) --范围分区,id不小于10000
14 );
表已创建。
该例首先按照范围进行分区,然后对子分区按照HASH进行分区,根据name列的hash值确定该行分布在tbsp_1或tbsp_2某个表空间上,
关键字是Interval,Interval分区是Oracle 11g版本新引入的分区方法,是范围分区的一种增强功能,可以实现equi_sized范围分区的自动化。创建的分区作为元数据,只有最开始的分区是永久分区。随着数据的增加会分配更多的部分,并自动创建新的分区和本地索引。
-----首先创建一个表saleRecord,然后为该表创建Interval分区
SQL> create table saleRecord
2 (
3 id number primary key, --编号
4 goodsname varchar2(50), --商品名称
5 saledate date, --销售日期
6 quantity number --销售量
7 )
8 partition by range(saledate) --以销售日期为分区键
9 interval (numtoyminterval(1,'year')) --Interval分区实现按年份进行自动分区
10 (
11 --设置分区键值日期小于2012-01-01
12 partition par_fist values less than (to_date('2012-01-01','yyyy-mm-dd'))
13 );
表已创建。
在上面代码中,函数NUMTOYMINTERVAL的功能是将数字转换成INTERVAL YEAR TO MONTH 文字(‘YEAR’or ‘MONTH’)。
说明:进行Interval分区的表格有传统的范围部分和自动生成的Interval部分。对于已经进行了范围分区的表格,可以通过使用ALTER TABLE命令的SET INTERVAL选项扩展成为Interval分区的表格。
对表进行分区设计时,首先要考虑和分析分区表中每个分区的数据量,其次要为每个分区创建相应的表空间。
1.识别大表
一般来说,数据占用存储空间大的表就是大表,系统架构师要做到的就是如何确定哪些表属于大表。如果要在目前运行的系统上进行表数据量分析,那么主要采用ANALYZE TABLE语句进行分析,然后查询数据字典获得相应的数据量;如果是一个正在进行需求分析的表,则只能采用估计的方法了。
2.大表如何分区
大表一般可以按时间分区,比如,如果按照月份分区,则需要为每个月创建一个数据表空间;如果按照季度分区;则一年要创建4个表空间;如果要存放5年用的表空间,则需要创建20个表空间。
3.分区的表空间规划
分区方法确定后,就要着手创建表空间,创建表空间前要对每个表空间的大小进行估算。如若每个季度的数据为100MB,则最好创建120MB的季度用表空间。另外,还要考虑数据量的增长,如当年的数据每季度是100MB,则是下一年可能要增长20%~30%,这些变化都要在表空间的大小上给予考虑。
对于已经存在表分区的某个表,如果要添加一个新的表分区,通常使用ALTER TABLE…ADD PARATITION语句,下面来看一个例子。
----在客户信息表clients中,添加一个省份为“河北省”的表分区
SQL> alter table clients
2 add partition hebei values('河北省')
3 storage(initial 10K next 20k) tablespace tbsp_1
4 nologging;
表已更改。
上面的例子不仅增加了分区“hebei”,而且给增加的分区指定了存储属性。
Oracle可以对表和索引进行分区,也可以对分区进行合作,从而减少散列分区或者复合分区的个数。在合并表分区之后,Oracle系统将做以下处理:
(1)在合并分区时,HASH列函数将分区内容分布到一个或多个保留分区中;
(2)原来内容所在的分区完全被清除;
(3)与分区对应的索引也被清除;
(4)将一个或多个索引的本地索引分区标识为不可用(UNSABLE);
(5)需要对不可用的索引进行重建。
下面将讲解如何合并散列分区和复合分区。
1.合并散列分区
使用ALTER TABLE…COALESCE PARTITION语句可以完成HASH列分区的合并
----合并person分区表中的一个HASH分区 SQL> alter table person coalesce partition;
表已更改。
2.合并复合分区
可以使用ALTER TABLE …MODIFY语句实现将某个子分区的内容重新分配到一个或者多个保留的子分区中
----把person2分区表中的par3分区合并到其它保留的子分区中,代码及运行结果如下。 SQL> alter table person2 modify partition par3 coalesce subpartition;
表已更改。
可以从范围分区或复合分区中删除分区。但是散列分区和复合分区的散列子分区,只能通过合并来达到删除的目的。
1.删除一个表分区
可以使用ALTER TABLE…DROP PARTITION语句删除范围分区和复合分区。删除分区时,该分区的数据也被删除。如果不希望删除数据,则必须采用合并分区的方法。
----把ware_retail_part分区表中的par_04分区删除。 SQL> alter table ware_retail_part drop partition par_04;
表已更改。
2.删除有数据和全局索引的表分区
如果分区表中包含了数据,并且在表中定义了一个或者多个全局索引,可以使用ALTER TABLE…DROP PARTITION语句删除表分区,这样可以保留全局索引,但是索引会被标识为不可用(UNUSABLE),因而需要重建索引。
----删除ware_retail_part分区表中的par_04分区,然后重建索引ware_index。 SQL> alter table ware_retail_part drop partition par_04;
表已更改
SQL> alter index ware_index rebuild;
索引已更改。
在上面的例子中,如果ware_index是范围分区的全局索引,那么就需要重建所有索引的分区:
alter index ware_index rebuild index_01;
alter index ware_index rebuild index_02;
alter index ware_index rebuild index_03;
3.使用DELETE和ALTER TABLE…DROP PARTITION语句。
在执行ALTER TABLE…DROP PARTITION语句前首先执行DELETE语句来删除分区的所有数据行,然后执行ALTER TABLE…DROP PARTITION语句,但是执行DELETE语句时需要更新全局索引。
----首先删除ware_retail_part分区表中第四季度的数据,然后再删除第四季度数据对应的par_04分区。
SQL> delete from ware_retail_part where retail_date >= to_date('2011-10-01','yyyy-mm-dd');
已删除513行。
SQL> alter table ware_retail_part drop partition par_04;
表已更改
4.删除具有完整性约束的分区
如果分区的表具有完整性约束,则可以采用以下两种办法。
(1)首先禁止完整性约束,然后执行ALTER TABLE…DROP PARTITION,最后激活约束,下面来看一个例子。
-----首先禁用books_1表的主键约束BOOK_PK,然后删除books_1表的分区part_01,最后激活books_1表的主键约束BOOK_PK。
SQL> alter table books_1 disable constraints BOOK_PK;
表已更改。
SQL> alter table books_1 drop partition part_01;
表已更改
SQL> alter table books_1 enable constraints BOOK_PK;
表已更改。
(2)首先执行DELETE语句删除分区中的行,然后用ALTER TABLE…DROP PARTITION语句删除分区。
-----首先删除books_1表中part_01分区中的所有记录,然后再删除part_01分区。
SQL> delete from books_1 where bookno < 1000;
已删除12行。
SQL> alter table books_1 drop partition part_01;
表已更改
用户可以使用MERGE PARTITION语句将相邻的范围分区合并在一起变为一个新的分区:该分区继承原来两个分区的边界;原来的两个分区与相应的索引一起被删除掉;如果被合并的分区非空,则该分区被标识为UNSABLE;不能对HASH分区表执行MERGE PARTITION语句。
并入范围分区是将两个以上的分区合并到一个存在的分区中,合并后一般索引要重建。为了便于大家对并入操作的理解,下面来看一个比较完整的例子。
—-首先在sales表中创建4个范围分区,然后再将第3个分区并入到第4个分区中。
(1)首先创建一个销售记录表sales,然后对该表的记录按照销售日期(即季度)分为4个范围分区,代码及运行结果如下:
SQL> create table sales --创建一个销售记录表
2 (
3 id number primary key, --记录编号
4 goodsname varchar2(10), --商品名
5 saledate date --销售日期
6 )
7 partition by range(saledate) --按照日期分区
8 (
9 --第一季度数据
10 partition part_sea1 values less than(to_date('2011-04-01','yyyy-mm-dd')) tablespace tbsp_1,
11 --第二季度数据
12 partition part_sea2 values less than(to_date('2011-07-01','yyyy-mm-dd')) tablespace tbsp_2,
13 --第三季度数据
14 partition part_sea3 values less than(to_date('2011-10-01','yyyy-mm-dd')) tablespace tbsp_1,
15 --第四季度数据
16 partition part_sea4 values less than(to_date('2012-01-01','yyyy-mm-dd')) tablespace tbsp_2
17 );
表已创建。
(2)在sales表中创建局部索引,代码及运行结果如下:
SQL> create index index_3_4 on sales(saledate)
2 local(
3 partition part_seal tablespace tbsp_1,
4 partition part_sea2 tablespace tbsp_2,
5 partition part_sea3 tablespace tbsp_1,
6 partition part_sea4 tablespace tbsp_2
7 );
索引已创建。
(3)使用ALTER TABLE…MERGEPARTITION语句把第3个分区并入到第4个分区中,代码及运行结果如下:
SQL> alter table sales merge partitions part_sea3,part_sea4 into partition part_sea4;
表已更改。
(4)最后重新建立局部索引,代码及运行结果如下:
SQL> alter table sales modify partition part_sea4 rebuild unusable local indexes;
表已更改。
若要删除一个重做日志组,需要使用带有 ALTER DATABASE DROP LOGFILE语句。下面来看一个例子。
----删除数据库中编号为5的日志组,代码及运行结果如下。 SQL> alter database drop logfile group 5;
数据库已更改。
与删除指定的日志文件相同,删除日志文件组也只是在数据字典和控制文件中将日志文件组的信息删除,而对应的物理文件并没有删除,若要删除,可以采取手动删除的方式。
3.清空重做日志文件
清空重做日志文件实际上就是将日志文件中的内容清空,这相当于删除原有的日志文件,重新创建新的日志文件。即使数据库只有两个重做日志文件组,甚至要清空的重做日志组处于CURRENT状态,也都可以成功执行清空操作。
清空日志文件,需要使用“ALTER DATABASE CLEAR LOGFILE”语句,下面来看一个例子。
----清空数据库中编号为4的日志组中所有日志文件的内容。 SQL> alter database clear logfile group 4;
数据库已更改。
说明:如果要清空的重做日志文件组尚未归档,则必须在使用“ALTER DATABASE CLEAR UNARCHIVED LOGFILE”语句。
Oracle索引分区分为本地索引分区和全局索引分区两种。全局索引不反映基础表的结构,因此,若要分区就只能进行范围分区。而局部索引反映基础表的结构,因此,对表的分区或子分区进行维护时,系统会自动对本地索引的分区进行维护,而不需要对本地索引的分区进行维护。
本地索引分区就是使用和分区表同样的分区键进行分区的索引,也就是说,索引分区所采用的列与该表的分区所采用的列是相同的。本地索引分区有如下优点:
位图索引仅由本地索引支持。
若要创建本地索引分区,可以使用CREATE INDEX…带LOCAL子句。接下来,通过一个实例来讲解创建本地索引分区的完整过程。
首先创建一个表分区,然后根据这个表分区创建本地索引分区,操作步骤及代码如下。
(1)准备好所需要的表空间
使用CREATE TABLESPACE语句创建3个表空间,这3个表空间应放在不同的磁盘分区上,分别是ts_1、ts_2、ts_3,代码如下:
SQL> create tablespace ts_1 datafile 'D:\OracleFiles\OracleData\ts1.dbf'
2 size 10m
3 extent management local autoallocate;
表空间已创建。
SQL> create tablespace ts_2 datafile 'E:\OracleFiles\OracleData\ts2.dbf'
2 size 10m
3 extent management local autoallocate;
表空间已创建。
SQL> create tablespace ts_3 datafile 'F:\OracleFiles\OracleData\ts3.dbf'
2 size 10m
3 extent management local autoallocate;
表空间已创建。
(2)创建一个存储学生成绩的分区表studentgrade,该表共有3个分区,分别位于表空间ts_1、ts_2和ts_3上,代码及运行结果如下:
SQL> create table studentgrade
2 (
3 id number primary key, --记录id
4 name varchar2(10), --学生名称
5 subject varchar2(10), --学科
6 grade number --成绩
7 )
8 partition by range(grade)
9 (
10 --小于60分,不及格
11 partition par_nopass values less than(60) tablespace ts_1,
12 --小于70分,及格
13 partition par_pass values less than(70) tablespace ts_2,
14 --大于或等于70分,优秀
15 partition par_good values less than(maxvalue) tablespace ts_3
16 );
表已创建。
(3)接下来,根据表分区创建本地索引分区,与表分区一样,索引分区也是三个分区(p1、p2、p3)。
SQL> create index grade_index on studentgrade(grade)
2 local --根据表分区创建本地索引分区
3 (
4 partition p1 tablespace ts_1,
5 partition p2 tablespace ts_2,
6 partition p3 tablespace ts_3
7 );
索引已创建。
(4)最后,用户可以通过查询DBA_IND_PARTITIONS视图来查看索引分区信息,代码如下:
SQL> select partition_name,tablespace_name from dba_ind_partitions where index_name = 'GRADE_INDEX';
全局索引就是没有与分区表由相同分区键的分区索引。当分区中出现许多事务并且要保证所有分区中的数据记录唯一时,采用全局索引分区。
无论表是否采用分区,都可以对表采用全局索引分区。此外,不能对Cluster表、位图索引采用全局索引分区。下面通过两个简单的例子来演示全局索引分区的创建。
----以Books表的saleprice列为索引列和分区键,创建一个范围分区的全局索引。
SQL> create index index_saleprice on Books(saleprice)
2 global partition by range(saleprice)
3 (
4 partition p1 values less than (30),
5 partition p2 values less than (50),
6 partition p3 values less than (maxvalue)
7 );
索引已创建。
-----以Books表的ISBN列为索引列和分区键,创建一个HASH分区的全局索引。
SQL> create index index_ISBN on books(ISBN)
2 global partition by hash(ISBN);
索引已创建。
对索引分区进行维护,应该使用ALTER INDEX语句,其对应的子句如表所示。与表分区不同的是,索引分区分为两种类型,全局索引和本地索引。
在管理索引分区的各种操作中,常用的操作主要包括删除索引分区和重命名索引分区,下面将通过具体的实例来演示这两种管理操作。
1.删除索引分区
可通过ALTER INDEX…DROP PARTITION语句来删除索引分区,例子如下。
----在books表的index_saleprice索引中,使用alter index…drop partition语句删除其中的索引分区p2。
SQL> alter index index_saleprice drop partition p2;
索引已更改。
注意:对于全局索引分区,不能删除索引的最高分区,否则系统会提示错误。
在删除若干索引分区之后,如果只剩余一个索引分区,则需要对这个分区进行重建,重建分区可以使用ALTER INDEX…REBUILD PARTITION语句来实现,例子如下。
----在books表的index_saleprice索引中,删除其中的p2和p1索引分区,然后使用alter index…rebuild partition语句重建索引分区p3。
SQL> alter index index_saleprice drop partition p2;
索引已更改。
SQL> alter index index_saleprice drop partition p1;
索引已更改。
SQL> alter index index_saleprice rebuild partition p3;
索引已更改。
2.重命名索引分区
重命名索引分区与重命名索引的语法格式比较接近,其语法格式如下:
alter index index_name rename partition partition_old_name to partition_new_name
参数说明如下:
index_name:索引名称。
partition_old_name:原索引分区名称
partition_new_name:新索引分区名称。
下面通过一个例子来演示如何重命名索引分区。
----在index_saleprice索引中,使用alter index…rename partition重命名索引分区p3。
SQL> alter index index_saleprice rename partition p3 to p_new;
索引已更改。
(1)打开SQL*Plus,以system身份登录。登录成功后,在SQL*Plus中输入如下代码创建用户表,代码及运行结果如下:
SQL> create table booksell( 2 bookid number primary key, 3 bookname varchar2(30), 4 bookprice float, 5 bookconcern varchar2(30), 6 count number)
7 partition by range(bookid)
8 (
9 partition par_01 values less than(10),
10 partition par_02 values less than(20),
11 partition par_03 values less than(30),
12 partition par_04 values less than(40)
13 );
表已创建。