Oracle 表分区笔记

时间:2022-07-22
本文章向大家介绍Oracle 表分区笔记,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

当表中的数据量不断增大,查询数据的速度就会变慢,应用程序的性能就会下降,这时就应该考虑对表进行分区。表进行分区后,逻辑上表仍然是一张完整的表,只是将表中的数据在物理上存放到多个表空间(物理文件上),这样查询数据时,不至于每次都扫描整张表。

优点

  1. 数据查询:数据被存储到多个文件上,减少了I/O负载,查询速度提高。
  2. 数据修剪:保存历史数据非常的理想。
  3. 备份:将大表的数据分成多个文件,方便备份和恢复。
  4. 并行性:可以同时向表中进行DML操作,并行性性能提高,均衡I/O:可以把不同的分区映射到磁盘以平衡I/O,改善整个系统性能。
  5. 增强可用性:如果表的某个分区出现故障,表在其他分区的数据仍然可用;
  6. 维护方便:如果表的某个分区出现故障,需要修复数据,只修复该分区即可;
  7. 改善查询性能:对分区对象的查询可以仅搜索自己关心的分区,提高检索速度。
  8. 需要注意的是包含LONG、LONGRAW数据类型的表不能分区,如果表格大于2G需要考虑分区。

种类

​ (1)范围分区(range)——我们这篇博文的内容;

​ (2)哈希分区(hash);

​ (3)列表分区(list);

​ (4)范围-哈希复合分区(range-hash);

​ (5)范围-列表复合分区(range-list)。

Range 分区

Range分区是应用范围比较广的表分区方式,它是以列的值的范围来做为分区的划分条件,将记录存放到列值所在的range分区中。

如按照时间划分,2010年1月的数据放到a分区,2月的数据放到b分区,在创建的时候,需要指定基于的列,以及分区的范围值。

在按时间分区时, 如果某些记录暂无法预测范围,可以创建 maxvalue 分区,所有不在指定范围内的记录都会被存储到 maxvalue 所在分区中。

函数

numtodsinterval

numtodsinterval(<x>,<c>),其中x是一个数字,c是一个字符串,

表明x的单位,这个函数把x转为interval day to second数据类型 常用的单位有 (dayhourminutesecond)

select sysdate,sysdate+numtodsinterval(3,'hour') as res from dual;

分区索引

对于分区索引,需要区分创建的是全局索引(global index)还是本地索引(local index)。

全局索引(global index)

即可以分区,也可以不分区。即可以建range 分区,也可以建hash 分区, 即可建于分区表,又可创建于非分区表上,就是说,全局索引是完全独立的,因此它也需要我们更多的维护操作。

特点:

  1. 全局索引的分区键和分区数和表的分区键和分区数可能都不相同,表和全局索引的分区机制不一样。
  2. 全局索引可以分区,也可以是不分区索引,全局索引必须是前缀索引,即全局索引的索引列必须是以索引 分区键作为其前几列。
  3. 全局分区索引的索引条目可能指向若干个分区,因此,对于全局分区索引,即使只动,截断一个分区中 的数据,都需要rebulid若干个分区甚至是整个索引。
  4. 全局索引多应用于oltp系统中。
  5. 全局分区索引只按范围或者散列hash分区,hash分区是10g以后才支持。
  6. oracle9i以后对分区表做move或者truncate的时可以用update global indexes语句来同步 更新全局分区索引,用消耗一定资源来换取高度的可用性。
  7. 表用a列作分区,索引用b做局部分区索引,若where条件中用b来查询,那么oracle会扫描所有的 表和索引的分区,成本会比分区更高,此时可以考虑用b做全局分区索引 。

本地索引(local index)

其分区形式与表的分区完全相同,依赖列相同,存储属性也相同。 对于本地索引,其索引分区的维护自动进行,就是说你add/drop/split/truncate 表的分区时, 本地索引会自动维护其索引分区。

特点:

  1. 局部索引一定是分区索引,分区键等同于表的分区键,分区数等同于表的分区说,一句话,局部索引的 分区机制和表的分区机制一样。
  2. 如果局部索引的索引列以分区键开头,则称为前缀局部索引。
  3. 如果局部索引的列不是以分区键开头,或者不包含分区键列,则称为非前缀索引。
  4. 前缀和非前缀索引都可以支持索引分区消除,前提是查询的条件中包含索引分区键。
  5. 局部索引只支持分区内的唯一性,无法支持表上的唯一性,因此如果要用局部索引去给表做唯一性约束, 则约束中必须要包括分区键列。
  6. 局部分区索引是对单个分区的,每个分区索引只指向一个表分区,全局索引则不然,一个分区索引能指向n个表分区,同时,一个表分区,也可能指向n个索引分区,对分区表中的某个分区做truncate 或者move,shrink等,可能会影响到n个全局索引分区,正因为这点,局部分区索引具有更高的可用性。
  7. 位图索引只能为局部分区索引。
  8. 局部索引多应用于数据仓库环境中。

创建本地索引示例

create index zt_wgxx_re_date_time on zt_wgxx(RECEIVE_DATE_TIME) local

常见错误

ORA-14060

创建好分区表后,分区字段的属性不能修改的,否则会报错ORA-14060: 不能更改表分区列的数据类型或长度

ORA-14751

Oracle 11g中推出的Interval特性,是针对Range类型分区的一种功能拓展,Interval支持的Range分区键类型只有numberdatetimestamp三种类型。其他类型字段做alter table tablename set INTERVAL()时会报错ORA-14751: 间隔分区表的分区列的数据类型无效

ORA-14300

当START_TIME的值为NULL时,然后向分区对象表插入数据,数据库不知道将该条记录插入到哪个分区,导致报错ORA-14300: 分区关键字映射到超出允许的最大分区数的分区

ORA-14752

ORA-14752: 间隔表达式不是正确类型的常数,应该查看是否写错了,日和周的是 NUMTODSINTERVAL,年的和月的 是 NUMTOYMINTERVAL

ORA-14767

ORA-14767: 无法使用现有上限指定此间隔,你时间写的不对,时间不可以超过指定时间,避免这种错误其实很简单,最简单的是利用28日作为分区上限

分区表创建示例

先说下,里面有两个地方需要注意,一个是分区字段名,这里用的是:WORKDATE,需要有个分区字段; 另外一个是划分分区时间的 less than(XXXX),这个时间是比较特殊,超过这个时间会自动创建额外的分区,但暂时这么理解吧,我一般管它叫默认时间,也可以创建多个,但是我都只创建一个,后续会自增。 对于是月的,年的建议设置时间为每个月的01月1日。

创建日分区表

create table TTT(WORKDATE date)
PARTITION BY RANGE (WORKDATE) INTERVAL (NUMTODSINTERVAL(1,'day'))
(    partition P_1995 values less than (TO_DATE('1995-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS')));

创建月分区表

create table TTT(WORKDATE date)
PARTITION BY RANGE (WORKDATE) INTERVAL (NUMTOYMINTERVAL(1,'month'))
(    partition P_1995 values less than (TO_DATE('1995-12-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS')));

创建年分区表

create table TTT(WORKDATE date)
PARTITION BY RANGE (WORKDATE) INTERVAL (NUMTOYMINTERVAL(1,'year'))
(    partition P_1995 values less than (TO_DATE('1995-12-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS')));

类型示例

DATE类型

DATE类型:建立的分区表说明2003-1-1之前的数据放入P01分区中,之后的数据每天一个分区

CREATE TABLE tablename (hid number,hdate date)

PARTITION BY RANGE(hdate) INTERVAL (NUMTODSINTERVAL(1, 'DAY'))

( PARTITION p01 VALUES LESS THAN (to_date('2003-1-1','yyyy-mm-dd')));

number类型

number类型:建立的分区表说明100之前的数据放入P01分区中,之后的数据每100放入一个新一个分区,比如102放入一个分区p02,203放入一个分区p03 如果只有100以内的数据,还没有大于100的数据,直接插入1111则一样自动建立一个分区,USER_TAB_PARTITIONS.HIGHVALUE显示为1200,再插入103一样会自动建立一个分区,USER_TAB_PARTITIONS.HIGHVALUE显示为200

CREATE TABLE tablename (hid number,hdate date)

PARTITION BY RANGE(hid) INTERVAL (100)

( PARTITION p01 VALUES LESS THAN (100));

当如如果建表的时候没有指定 INTERVAL

--修改每100为单位分区则可以手工修改如下
alter table tablename set INTERVAL (100);

--修改每天分区则可以手工修改如下
alter table tablename set INTERVAL (NUMTODSINTERVAL(1, 'DAY'));

--修改为每周分区则如下
alter table tablename set INTERVAL(numtodsinterval(7,'day'));

--修改为每月分区则如下
alter table tablename set INTERVAL(numtoyminterval(1,'MONTH'));

--修改为每年分区则如下 
alter table tablename set INTERVAL(numtoyminterval(1,'YERA'));
--numtodsinterval(<x>,<c>) ,x是一个数字,c是一个字符串,c只能是'DAY'、'HOUR'、'MINUTE'、'SECOND'
--numtoyminterval(<x>,<c>) ,x是一个数字,c是一个字符串,c只能是'YEAR'、'MONTH'

实操

create table ZT_WGXX
(
    ID                VARCHAR2(50) not null
        constraint PK_ZT_WGXX
            primary key,
    PROJECT_ID        VARCHAR2(50),
    CAR_ID            VARCHAR2(50),
    TRAFFICCOMPANY_ID VARCHAR2(50),
    TYPE              VARCHAR2(50),
    WGNR              VARCHAR2(2000),
    CREATE_DATE       DATE         not null,
    CREATE_BY         VARCHAR2(50) not null,
    UPDATE_DATE       DATE,
    UPDATE_BY         VARCHAR2(50),
    REMARKS           VARCHAR2(250),
    DEL_FLAG          NUMBER(4),
    MAP_X             VARCHAR2(50),
    MAP_Y             VARCHAR2(50),
    SYSTEM_FLAG       VARCHAR2(50),
    ARTIFICIAL_FLAG   VARCHAR2(50),
    RECEIVE_DATE_TIME DATE,
    RECEIVE_DATE      VARCHAR2(250),
    RECEIVE_DATE_END  VARCHAR2(250),
    XWFSDD_NAME       VARCHAR2(500)
) PARTITION BY RANGE (RECEIVE_DATE_TIME) INTERVAL (NUMTODSINTERVAL(1, 'day'))
(PARTITION ZT_WGXX_pt01 VALUES LESS THAN (to_date('2000-1-1', 'yyyy-mm-dd')));

说明:

​ INTERVAL 分区,按天分区,需要用到函数NUMTODSINTERVAL