相比很多同学都做过历史数据备份的工作,可能方式有很多种,在此说一种业务场景:有原始表a和历史表a_his两个表,每个月月初将a中上个月之前的历史数据,转移到a_his中。
可能一开始想到的方案就是使用insert into a_his select * from a where date<'上个月1号'; 然后delete froma where date<'上个月1号'; 。
且说这个方案逻辑上有没有问题,其实没有问题(一开始我也是这么做的)。但是当你有不止一个a表,且每个表的数据量都在百万千万甚至亿级的时候,你会发现,你的这个insert into 和delete 很消耗数据库的性能。
那么怎么弄才能快,而且不消耗性能。
1、首先原始表a需要根据业务进行分区,而且分区的名称必须是有规则的,比如我的命名是以P_开头,xxxxMMyy结尾,示例:P_20170101,此处是根据date进行按月分区。 a_his表结构跟a一样,但是不用分区。完整的建表示例:
create table a(
vid varchar2(20),
vname varchar2(50),
vdate varchar2(10)
)
partition by range (vdate)
(
partition P_20170101 values less than ('2017-01-01'),
partition P_20170201 values less than ('2017-02-01')
);
如需要建索引,就建本地索引,分区表不建议建主键。
建索引的语句如下:create index IND_a_vid on a(vid) local nologging;
2、将用户授予建表及不限表空间权限。如grant create table,unlimited tablespace to testuser;
3、封装执行数据转移的存储过程。示例:
create or replace procedure proce_movedatadtl(
vtype in varchar2,-- 表名
tempworkdate in varchar2 --日期
)
as
tempstr varchar2(20);
tempsql varchar2(2000);
begin
tempstr:= replace(tempworkdate,'-','');
-- 根据日期,动态创建原始表名为vtype 的备份表,并指定表空间为test_bak,只创建表结构
tempsql:='create table '||vtype||'_'||tempstr||' tablespace test_bak as select * from '||vtype||' where 1=0';
execute immediate tempsql;
-- 将原始表的指定表分区数据转移到备份表中,执行之后该分区索引会失效
tempsql:='alter table '||vtype||' exchange partition P_'||tempstr||' with table '||vtype||'_'||tempstr;
execute immediate tempsql;
-- 重建原始表的指定表分区的索引
tempsql:='alter index IND_'||vtype||'_P_VID rebuild partition P_'||tempstr; --重建索引,否则该分区索引是失效的
execute immediate tempsql;
end;
/
4、创建可行执行的存储过程,可通过job,定时每月1号执行
create or replace procedure proce_movedata
as
tempworkdate varchar2(19);
begin
select to_char(add_months(sysdate,-1),'yyyy-MM')||'-01' into tempworkdate from dual; --根据当前时间查询上个月的第一天时间
proce_movedatadtl('a',tempworkdate);
end;
/
此处给出的方案是我这边根据业务指定的,其他如果不符合此业务模型的可能就不能完全套用这个,但是应该也会提供一种思路。
凡是代码就要符合业务方可。