分区表自动管理

整理一些分区表自动管理的脚本,以备不时之需

1、分区合并

create or replace procedure consumetest.sp_auto_partition_combine
/************************************************************************************
    Name:          sp_auto_partition_combine
    Purpose:       分区合并
    Output:        errcode(7100=true,8100=false),errstr
    Source table:  partition tables
  ************************************************************************************/
(v_errcode out number, v_errstr out varchar2) as
  v_minparti      varchar2(10); --实际的最小分区
  v_keepparti     varchar2(10); --保留的最小分区
  v_firstparti    varchar2(10);
  v_secondparti   varchar2(10);
  sqlcmd          varchar2(300);
  v_keepdaynum    number := 110;
  v_loopcount     number; --循环计数器
  v_exec_failinfo varchar2(500);

begin
  --step1:处理按天分区的分区表
  for c in (select distinct table_name as table_name
              from user_tab_partitions
             where partition_name <> 'PMAX'
               and table_name like '%_ITEMDETAIL%'
               and table_name like 'T%'
               and high_value_length = 83
               and length(partition_name) = 9            
             order by table_name) loop
    select min(partition_name),
           'P' || to_char(to_date(replace(max(partition_name), 'P', ''),
                                  'yyyy-mm-dd') - v_keepdaynum,
                          'yyyymmdd')
      into v_minparti, v_keepparti
      from user_tab_partitions
     where table_name = c.table_name
       and partition_name <> 'PMAX';
  
    if (v_minparti < v_keepparti) then
      select count('x')
        into v_loopcount
        from user_tab_partitions
       where table_name = c.table_name
         and partition_name >= v_minparti
         and partition_name <= v_keepparti;
    
      for p in 1 .. v_loopcount loop
        select min(partition_name), max(partition_name)
          into v_firstparti, v_secondparti
          from user_tab_partitions
         where table_name = c.table_name
           and rownum < 3
         order by partition_name;
      
        sqlcmd := 'alter table ' || c.table_name || ' merge partitions ' ||
                  v_firstparti || ',' || v_secondparti ||
                  ' into partition ' || v_secondparti;
      
        --execute immediate sqlcmd;
        dbms_output.put_line('sqlcmd = ' || sqlcmd);
      end loop;
    end if;
  end loop;

  v_errcode := 7100;
  v_errstr  := 'sp_wh_auto_merge_partitions mession complete!';

exception
  when others then
    begin
      dbms_output.put_line(sqlerrm);
      v_exec_failinfo := substr(SQLERRM, 1, 500);
      v_errcode       := 8100;
      v_errstr        := v_exec_failinfo;
    end;
end sp_auto_partition_combine;
/

2、分区自动扩展

create or replace procedure consumetest.sp_auto_partition_extend
/************************************************************************************
    Name:          sp_auto_partition_extend
    Purpose:       自动向前、向后扩展分区表分区(默认情况下扩展所有分区表,若想指定某张表则修改for循环条件即可)
    Input:         manageflag(0:向前扩展 1:向后扩展)
                   extenddate(eg.20140101)
    Output:        errcode(7100=true,8100=false),errstr
    Source table:  partition tables
  ************************************************************************************/
(v_manageflag in number, --0:向前扩展  1:向后扩展
 v_extendate  in varchar2, -- 指定向前扩展分区的起始时间或向后扩展分区的终止时间(eg.20140101)
 v_errcode    out number,
 v_errstr     out varchar2) as
  v_count             number; --计数器
  v_actual_ptnum      number; --除PMAX外分区表中已有分区数量(实际值)
  v_already_ptnum     number; --除PMAX外分区表中已有分区数量(理论值)  
  v_extenddate_num    number; --向前或向后扩展的分区数  
  buffer              varchar2(50);
  v_sqlcmd            varchar2(500);
  v_extenddate        varchar2(20);
  v_lost_pname        varchar2(10); --漏掉的分区名
  v_extend_pname      varchar2(10); --扩展分区
  v_str_lost_pname    varchar2(10); --漏掉分区的时间格式yyyymmdd
  v_next_lost_pname   varchar2(10); --漏掉的分区的前面的一个分区  
  v_extenddate_pname  varchar2(10); --向前扩展分区的起始分区或向后扩展分区的终止分区(eg.P20140101)
  v_str_extend_pname  varchar2(10); --下一个扩展分区(eg.20140101)
  v_next_extend_pname varchar2(10); --下一个扩展分区

begin
  buffer := 'begin dbms_output.enable(200000);end;';
  execute immediate buffer;

  if (instr(v_extendate, 'P', 1) > 0) then
    v_extenddate := replace(v_extendate, 'P', '');
  elsif (instr(v_extendate, 'p', 1) > 0) then
    v_extenddate := replace(v_extendate, 'p', '');
  elsif (instr(v_extendate, '_', 1) > 0) then
    v_extenddate := replace(v_extendate, '_', '');
  elsif (instr(v_extendate, '-', 1) > 0) then
    v_extenddate := replace(v_extendate, '-', '');
  elsif (length(v_extendate) = 8) then
    v_extenddate := v_extendate;
  else
    v_errcode := 8100;
    v_errstr  := ' Invalid parameter! ';
    return;
  end if;

  --在进行扩展分区操作前,先检查现有分区是否存在断档现象,如有,则补全
  for pt_info in (select table_name,
                         count('x') count_ptname,
                         max(partition_name) max_ptname,
                         min(partition_name) min_ptname
                    from user_tab_partitions
                   where partition_name <> 'PMAX'
                     and table_name like 'T%'
                     and high_value_length = 83
                     and length(partition_name) = 9
                  --and table_name = 'T_DW_IWM_GDB_CASHSTAT'
                   group by table_name
                   order by table_name) loop
    v_already_ptnum := to_date(substr(pt_info.max_ptname, 2, 8), 'yyyymmdd') -
                       to_date(substr(pt_info.min_ptname, 2, 8), 'yyyymmdd') + 1;
    v_actual_ptnum  := pt_info.count_ptname;
  
    --已有分区中存在分区断档现象
    if (v_already_ptnum > v_actual_ptnum) then
      for i in 2 .. v_already_ptnum loop
        v_lost_pname := 'P' ||
                        to_char(to_date(substr(pt_info.max_ptname, 2, 8),
                                        'yyyymmdd') - i + 1,
                                'yyyymmdd');
        select count('x')
          into v_count
          from user_tab_partitions
         where table_name = pt_info.table_name
           and partition_name = v_lost_pname;
      
        if (v_count = 0) then
          v_next_lost_pname := 'P' || to_char(to_date(substr(pt_info.max_ptname,
                                                             2,
                                                             8),
                                                      'yyyymmdd') - i + 2,
                                              'yyyymmdd');
          v_str_lost_pname  := substr(v_next_lost_pname, 2, 8);
          v_sqlcmd          := '';
          v_sqlcmd          := v_sqlcmd || 'ALTER TABLE ' ||
                               pt_info.table_name || ' split partition ' ||
                               v_next_lost_pname || ' at (to_date(''' ||
                               v_str_lost_pname ||
                               ''',''yyyymmdd'')) into (partition ' ||
                               v_lost_pname || ', partition ' ||
                               v_next_lost_pname || ')' ||
                               ' update global indexes ';
          dbms_output.put_line(' v_sqlcmd := ' || v_sqlcmd);
          --execute immediate v_sqlcmd;
        end if;
      end loop;
    end if;
  end loop;
  
  --开始进行分区扩展操作
  if (v_manageflag = 0) then
    for pt_info in (select table_name, min(partition_name) min_ptname
                      from user_tab_partitions
                     where partition_name <> 'PMAX'
                       and table_name like 'T%'
                       and high_value_length = 83
                       and length(partition_name) = 9
                    --and table_name = 'T_DW_IWM_GDB_CASHSTAT'
                     group by table_name
                     order by table_name) loop
    
      --自动向前扩展
      v_extenddate_pname := 'P' || v_extenddate;
    
      if (to_date(substr(pt_info.min_ptname, 2, 8), 'yyyymmdd') >
         to_date(substr(v_extenddate_pname, 2, 8), 'yyyymmdd')) then
        --计算向前扩展的分区数
        v_extenddate_num := to_date(substr(pt_info.min_ptname, 2, 8),
                                    'yyyymmdd') -
                            to_date(substr(v_extenddate_pname, 2, 8),
                                    'yyyymmdd');
        for i in 1 .. v_extenddate_num loop
          v_extend_pname      := 'P' || to_char(to_date(substr(pt_info.min_ptname,
                                                               2,
                                                               8),
                                                        'yyyymmdd') - i + 1,
                                                'yyyymmdd');
          v_next_extend_pname := 'P' || to_char(to_date(substr(pt_info.min_ptname,
                                                               2,
                                                               8),
                                                        'yyyymmdd') - i,
                                                'yyyymmdd');
          v_str_extend_pname  := substr(v_extend_pname, 2, 8);
          v_sqlcmd            := '';
          v_sqlcmd            := v_sqlcmd || 'ALTER TABLE ' ||
                                 pt_info.table_name || ' split partition ' ||
                                 v_extend_pname || ' at (to_date(''' ||
                                 v_str_extend_pname ||
                                 ''',''yyyymmdd'')) into (partition ' ||
                                 v_next_extend_pname || ', partition ' ||
                                 v_extend_pname || ')' ||
                                 ' update global indexes ';
          dbms_output.put_line(' v_sqlcmd := ' || v_sqlcmd);
          --execute immediate v_sqlcmd;
        end loop;
      end if;
    end loop;
  else
    --自动向后扩展
    for pt_info in (select table_name, max(partition_name) max_ptname
                      from user_tab_partitions
                     where partition_name <> 'PMAX'
                       and table_name like 'T%'
                       and high_value_length = 83
                       and length(partition_name) = 9
                     group by table_name
                     order by table_name) loop
    
      --自动向后扩展
      v_extenddate_pname := 'P' || v_extenddate;
    
      if (to_date(substr(pt_info.max_ptname, 2, 8), 'yyyymmdd') <
         to_date(substr(v_extenddate_pname, 2, 8), 'yyyymmdd')) then
        --计算向后扩展的分区数
        v_extenddate_num := to_date(substr(v_extenddate_pname, 2, 8),
                                    'yyyymmdd') -
                            to_date(substr(pt_info.max_ptname, 2, 8),
                                    'yyyymmdd');
        for i in 1 .. v_extenddate_num loop
          v_extend_pname      := 'P' || to_char(to_date(substr(pt_info.max_ptname,
                                                               2,
                                                               8),
                                                        'yyyymmdd') + i,
                                                'yyyymmdd');
          v_next_extend_pname := 'P' || to_char(to_date(substr(pt_info.max_ptname,
                                                               2,
                                                               8),
                                                        'yyyymmdd') + i + 1,
                                                'yyyymmdd');
          v_str_extend_pname  := substr(v_next_extend_pname, 2, 8);
          v_sqlcmd            := '';
          v_sqlcmd            := v_sqlcmd || 'ALTER TABLE ' ||
                                 pt_info.table_name ||
                                 ' split partition pmax ' ||
                                 ' at (to_date(''' || v_str_extend_pname ||
                                 ''',''yyyymmdd'')) into (partition ' ||
                                 v_extend_pname ||
                                 ', partition pmax) update global indexes ';
          dbms_output.put_line(' v_sqlcmd := ' || v_sqlcmd);
          --execute immediate v_sqlcmd;
        end loop;
      end if;
    end loop;
  end if; 

  commit;
  v_errcode := 7100;
  v_errstr  := ' sp_auto_partition_extend mission complete ! ';

exception
  when others then
    dbms_output.put_line(sqlerrm);
    v_errstr := substr(sqlerrm, 1, 400);
    select 8100 into v_errcode from dual;
end sp_auto_partition_extend;

3、移动分区表空间

create or replace procedure consumetest.sp_auto_partition_move(errcode out number,
                                                   errstr  out varchar2) is
  v_sqlcmd  varchar2(300);
  v_ts_name varchar2(20) := 'TS_YXW_HISTORY';
  cursor c1 is
    select table_name, partition_name
      from user_tab_partitions
     where partition_name between 'P201206' and 'P201207'
       and composite = 'NO'
       and tablespace_name <> v_ts_name;

  /*cursor c2 is
  select b.table_name,
         b.partition_name,
         b.subpartition_name,
         b.tablespace_name
    from user_tab_partitions a, user_tab_subpartitions b
   where a.table_name = b.table_name
     and a.partition_name = b.partition_name
     and b.partition_name between 'P201108' and 'P20110831'
     and b.tablespace_name <> v_ts_name;*/
begin
  FOR ptname in c1 LOOP
    v_sqlcmd := 'alter table ' || ptname.table_name || ' move partition ' ||
                ptname.partition_name || ' tablespace ' || v_ts_name ||
                ' compress';
    execute immediate v_sqlcmd;
  END LOOP;

  /*FOR ptname in c2 LOOP
    v_sqlcmd := 'alter table ' || ptname.table_name ||
                ' move subpartition ' || ptname.subpartition_name ||
                ' tablespace ' || v_ts_name || ' compress';
    execute immediate v_sqlcmd;
  END LOOP;*/

  errcode := 7100;
  errstr  := 'sp_auto_partition_move mession complete!';

EXCEPTION
  WHEN OTHERS THEN
    errcode := 8100;
    errstr  := 'sp_auto_partition_move mession fails!';
end sp_auto_partition_move;

4、自动添加分区并记录日志

create or replace procedure consumetest.sp_auto_partition_task
/************************************************************************************
    Name:          sp_auto_partition_task
    Purpose:       每天自动向前扩展一个分区,并保证每个表分区数量不变
    Output:        errcode(7100=true,8100=false),errstr
  ************************************************************************************/
(current_day date, keep_daynum number, keep_monthnum number) as
  v_pt_endday_time        date; --当前最大分区数据结束时间
  v_pt_endmonth_time      date; --当前最大分区数据结束时间
  v_pday                  date; --新分区数据区间时间(v_pday 00:00:00--23:59:59)
  v_pmonth                date; --新分区数据区间时间(月)
  v_pt_endday_time_next   char(8); --新分区数据结束时间(天)
  v_pt_endmonth_time_next char(8); --新分区数据结束时间(月)
  day_num                 number; --新分区结束时间与当前最大分区结束时间天数差
  month_num               number; --新分区结束时间与当前最大分区结束时间月份数差
  v_ptname_next           varchar2(9); --新分区名
  sqlcmd                  varchar2(300);
  v_sql                   varchar2(300);

begin
  --step1:处理按天分区的分区表
  v_pday := trunc(current_day + keep_daynum, 'dd'); ----分区日期(添加当前时间之后keep_daynum天的分区)
  for p_day_partition in (select table_name, max(partition_name) max_ptname
                            from user_tab_partitions
                           where partition_name <> 'PMAX'
                             and table_name like 'T%'
                             and high_value_length = 83
                             and length(partition_name) = 9
                           group by table_name
                           order by max_ptname) loop
    v_pt_endday_time := to_date(substr(p_day_partition.max_ptname, 2, 9),
                                'yyyymmdd') + 1;
  
    if (v_pday = v_pt_endday_time) then
      v_pt_endday_time_next := to_char(v_pt_endday_time + 1, 'yyyymmdd'); --下一个分区结束点
      v_ptname_next         := 'P' || to_char(v_pday, 'yyyymmdd'); --下一个分区的分区名(P+分区日期)
      sqlcmd                := 'alter table ' || p_day_partition.table_name ||
                               ' split partition pmax at ( to_date(''' ||
                               v_pt_endday_time_next ||
                               ''',''yyyymmdd'')) into (partition ' ||
                               v_ptname_next ||
                               ', partition pmax) update global indexes';
      dbms_output.put_line(sqlcmd);
      --execute immediate sqlcmd;
    elsif v_pt_endday_time < v_pday then
      --如果要添加的分区与已有的最大分区之间有漏掉的分区
      day_num := v_pday - v_pt_endday_time; --漏掉的天数
      for i in 1 .. day_num loop
        v_pt_endday_time_next := to_char(v_pt_endday_time + 1, 'yyyymmdd'); --下一个分区结束点
        v_ptname_next         := 'P' ||
                                 to_char(v_pt_endday_time, 'yyyymmdd'); --下一个分区的分区名(P+分区日期)
        sqlcmd                := 'alter table ' ||
                                 p_day_partition.table_name ||
                                 ' split partition pmax at ( to_date(''' ||
                                 v_pt_endday_time_next ||
                                 ''',''yyyymmdd'')) into (partition ' ||
                                 v_ptname_next ||
                                 ', partition pmax) update global indexes';
        dbms_output.put_line(sqlcmd);
        --execute immediate sqlcmd;
      
        v_pt_endday_time := v_pt_endday_time + 1; --加一天
      end loop;
    else
      dbms_output.put_line('Today''s job has done,so didn''t split any day partition!');
      exit;
    end if;
  end loop;

  --step2:处理按月分区的分区表
  v_sql := 'select trunc(sysdate,''mm'') + interval ''' || keep_monthnum ||
           ''' month from dual';
  execute immediate v_sql
    into v_pmonth; --分区日期(添加当前时间之后keep_monthnum月的分区)
  for p_month_partition in (select table_name,
                                   max(partition_name) max_ptname
                              from user_tab_partitions
                             where partition_name <> 'PMAX'
                               and table_name like 'T%'
                               and high_value_length = 83
                               and length(partition_name) = 7
                             group by table_name
                             order by max_ptname) loop
  
    v_pt_endmonth_time := to_date(substr(p_month_partition.max_ptname, 2, 7),
                                  'yyyymm');
    if (v_pmonth = v_pt_endmonth_time) then
      v_pt_endmonth_time_next := to_char(v_pt_endmonth_time + interval '2'
                                         month,
                                         'yyyymm'); --下一个分区结束点
      v_ptname_next           := 'P' || to_char(v_pt_endmonth_time +
                                                interval '1' month,
                                                'yyyymm'); --下一个分区的分区名(P+分区月份)
      sqlcmd                  := 'alter table ' ||
                                 p_month_partition.table_name ||
                                 ' split partition pmax at ( to_date(''' ||
                                 v_pt_endmonth_time_next ||
                                 ''',''yyyymm'')) into (partition ' ||
                                 v_ptname_next ||
                                 ', partition pmax) update global indexes';
      dbms_output.put_line(sqlcmd);
      --execute immediate sqlcmd;     
    elsif v_pt_endmonth_time < v_pmonth then
      --如果要添加的分区与已有的最大分区之间有漏掉的分区
      select months_between(v_pmonth, v_pt_endmonth_time)
        into month_num --漏掉的月数
        from dual;
      for i in 1 .. month_num loop
        v_pt_endmonth_time_next := to_char(v_pt_endmonth_time + interval '2'
                                           month,
                                           'yyyymm'); --下一个分区结束点
        v_ptname_next           := 'P' || to_char(v_pt_endmonth_time +
                                                  interval '1' month,
                                                  'yyyymm'); --下一个分区的分区名(P+分区月份)
        sqlcmd                  := 'alter table ' ||
                                   p_month_partition.table_name ||
                                   ' split partition pmax at ( to_date(''' ||
                                   v_pt_endmonth_time_next ||
                                   ''',''yyyymm'')) into (partition ' ||
                                   v_ptname_next ||
                                   ', partition pmax) update global indexes';
        dbms_output.put_line(sqlcmd);
        --execute immediate sqlcmd;
        v_pt_endmonth_time := v_pt_endmonth_time + interval '1' month; --加一个月
      end loop;
    else
      dbms_output.put_line('Today''s job has done,so didn''t split any month partition!');
      exit;
    end if;
  end loop;

exception
  when others then
    begin
      dbms_output.put_line(sqlerrm);
    end;
end sp_auto_partition_task;

5、移动分区表空间

create or replace procedure consumetest.sp_auto_tablespace_move is
  v_ts_name varchar2(15) := 'TS_TABLE_DATA';
  v_sqlcmd  varchar2(300);
  cursor c1 is
    select table_name, partition_name
      from user_tab_partitions
     where tablespace_name = 'TS_TABLE_HISTORY';
begin
  FOR ptname in c1 LOOP
    v_sqlcmd := 'alter table hr.' || ptname.table_name ||
                ' move partition ' || ptname.partition_name ||
                ' tablespace ' || v_ts_name || ' parallel compress';
    execute immediate v_sqlcmd;
  END LOOP;

EXCEPTION
  WHEN OTHERS THEN
    null;
end sp_auto_tablespace_move;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值