zoukankan      html  css  js  c++  java
  • 普通表改成分区表的方法

    有一系列普通表都有几十到几百GB这么大,数据从几亿到几十亿,现在想将这些表改造成分区表,用其中的时间或者其他字段来做分区,允许有一段停机时间来停这些表相关的应用,该如何做呢?

    思路:
    新建一张分区表,按日期建分区,确保分区表各字段和属性都和普通表一样。然后停应用,将普通表记录插入到分区表中。然后将普通表重命名,分区表命名成原表的名字,完成任务。

    将原表重命名为_yyyymmdd格式的表名:

    create or replace procedure p_rename_001(p_tab in varchar2) as
      /*
      功能:将原表重命名为_yyyymmdd格式的表名
      完善点:要考虑RENMAE的目标表已存在的情况,先做判断
      */
      v_cnt_re_tab   number(9) := 0;
      v_sql_p_rename varchar2(4000);
      yyyymmdd       varchar2(8);
    begin
      select to_char(sysdate, 'yyyymmdd') into yyyymmdd from dual;
    
      select count(*)
        into v_cnt_re_tab
        from user_objects
       where object_name = upper(p_tab || '_' || yyyymmdd);
      if v_cnt_re_tab = 0 then
        v_sql_p_rename := 'rename ' || p_tab || ' to ' || p_tab || '_' ||
                          yyyymmdd;
        -- DBMS_OUTPUT.PUT_LINE(v_sql_p_rename);--调试使用
        p_insert_log(p_tab,
                     'P_RENAME',
                     v_sql_p_rename,
                     '完成原表的重命名,改为_YYYYMMDD形式',
                     1);
        execute immediate (v_sql_p_rename); --这里无须做判断,rename动作真实完成!如果后续只是为生成脚本而不是真实执行分区操作,最后再把这个表RENAME回去!
      else
        raise_application_error(-20066,
                                '备份表' || p_tab || '_' || yyyymmdd ||
                                '已存在,请先删除或重命名该备份表后再继续执行!');
        -- DBMS_OUTPUT.PUT_LINE('备份表'||P_TAB||'_'||YYYYMMDD||'已存在');
      end if;
      dbms_output.put_line('操作步骤1(备份原表)-------将' || p_tab || ' 表RENMAE成 ' ||
                           p_tab || '_' || yyyymmdd || ',并删除其约束索引等');
    end p_rename_001;

    用CREATE TABLE AS SELECT 的方式从RENAME的_yyyymmdd表中新建出一个只有MAXVALUE的初步分区表:

    create or replace procedure p_ctas_002
    (
      p_tab         in varchar2,
      p_struct_only in number,
      p_deal_flag   in number,
      p_part_colum  in varchar2,
      p_parallel    in number default 4,
      p_tablespace  in varchar2
    ) as
      /*
      功能:用CREATE TABLE AS SELECT 的方式从RENAME的_yyyymmdd表中新建出一个只有MAXVALUE的初步分区表
      完善点:要考虑并行,nologging 的提速方式,也要考虑最终将NOLOGGING和PARALLEL恢复成正常状态
      */
      v_sql_p_ctas varchar2(4000);
    begin
      v_sql_p_ctas := 'create table ' || p_tab || ' partition by range ( ' ||
                      p_part_colum || ' ) (' ||
                      ' partition P_MAX  values less than (maxvalue))' ||
                      ' nologging parallel 4  tablespace ' || p_tablespace ||
                      ' as select /*+parallel(t,' || p_parallel || ')*/ *' ||
                      ' from ' || p_tab || '_' || yyyymmdd;
    
      if p_struct_only = 0 then
        v_sql_p_ctas := v_sql_p_ctas || ' where 1=2';
      else
        v_sql_p_ctas := v_sql_p_ctas || ' where 1=1';
      end if;
      --DBMS_OUTPUT.PUT_LINE(v_sql_p_ctas);--调试使用
      p_insert_log(p_tab, 'p_ctas', v_sql_p_ctas, '完成CTAS建初步分区表', 2, 1);
      p_if_judge(v_sql_p_ctas, p_deal_flag);
      v_sql_p_ctas := 'alter table ' || p_tab || ' logging';
      p_insert_log(p_tab,
                   'p_ctas',
                   v_sql_p_ctas,
                   '将新分区表修改回LOGGING属性',
                   2,
                   2);
      p_if_judge(v_sql_p_ctas, p_deal_flag);
      v_sql_p_ctas := 'alter table ' || p_tab || ' noparallel';
      p_insert_log(p_tab,
                   'p_ctas',
                   v_sql_p_ctas,
                   '将新分区表修改回NOPARALLEL属性',
                   2,
                   3);
      p_if_judge(v_sql_p_ctas, p_deal_flag);
      dbms_output.put_line('操作步骤2(建分区表)-------通过CTAS的方式从 ' || p_tab || '_' ||
                           yyyymmdd || ' 中新建' || p_tab || '表,完成初步分区改造工作');
    end p_ctas_002;

    对分区表进行分区SPLIT工作

    create or replace procedure p_split_part_003
    (
      p_tab            in varchar2,
      p_deal_flag      in number,
      p_part_nums      in number default 24,
      p_tab_tablespace in varchar2
    ) as
      /*
      功能:用CREATE TABLE AS SELECT 的方式新建出一个只有MAXVALUE的初步分区表进行SPLIT,
      按月份进行切分,默认p_part_nums产生24个分区,构造2年的分区表,第一个分区为当前月的
      上一个月
      */
      v_first_day        date;
      v_next_day         date;
      v_prev_day         date;
      v_sql_p_split_part varchar2(4000);
    begin
      select to_date(to_char(sysdate, 'yyyymm') || '01', 'yyyymmdd')
        into v_first_day
        from dual;
      for i in 1 .. p_part_nums loop
        select add_months(v_first_day, i) into v_next_day from dual;
        select add_months(v_next_day, -1) into v_prev_day from dual;
        v_sql_p_split_part := 'alter table ' || p_tab ||
                              ' split partition p_MAX at ' || '(to_date(''' ||
                              to_char(v_next_day, 'yyyymmdd') ||
                              ''',''yyyymmdd''))' || 'into (partition PART_' ||
                              to_char(v_prev_day, 'yyyymm') || ' tablespace ' ||
                              p_tab_tablespace || ', partition p_MAX)';
        -- DBMS_OUTPUT.PUT_LINE(v_sql_p_split_part);--调试使用
        p_insert_log(p_tab,
                     'p_split_part',
                     v_sql_p_split_part,
                     '分区表完成分区SPLIT工作',
                     3,
                     i);
        p_if_judge(v_sql_p_split_part, p_deal_flag);
      end loop;
      dbms_output.put_line('操作步骤3(分区操作)-------对新建的' || p_tab ||
                           '分区表完成分区SPLIT工作');
    end p_split_part_003;

    从_YYYYMMDD备份表中得到表的注释,为新分区表的表名增加注释

    create or replace procedure p_tab_comments_004
    (
      p_tab       in varchar2,
      p_deal_flag in number
    ) as
      /*
      功能:从_YYYYMMDD备份表中得到表的注释,为新分区表的表名增加注释
      */
      v_sql_p_tab_comments varchar2(4000);
      v_cnt                number;
    begin
      select count(*)
        into v_cnt
        from user_tab_comments
       where table_name = upper(p_tab) || '_' || yyyymmdd
         and comments is not null;
      if v_cnt > 0 then
        for i in (select *
                    from user_tab_comments
                   where table_name = upper(p_tab) || '_' || yyyymmdd
                     and comments is not null) loop
          v_sql_p_tab_comments := 'comment on table ' || p_tab || ' is ' || '''' ||
                                  i.comments || '''';
          -- DBMS_OUTPUT.PUT_LINE(v_sql_p_deal_tab_comments);--调试使用
          p_insert_log(p_tab,
                       'p_deal_comments',
                       v_sql_p_tab_comments,
                       '将新分区表的表的注释加上',
                       4,
                       1);
          p_if_judge(v_sql_p_tab_comments, p_deal_flag);
        end loop;
        dbms_output.put_line('操作步骤4(表的注释)-------对' || p_tab || '表增加表名的注释内容');
      else
        dbms_output.put_line('操作步骤4(表的注释)-------' || upper(p_tab) || '_' ||
                             yyyymmdd || '并没有表注释!');
      end if;
    end p_tab_comments_004;

    从_YYYYMMDD备份表中得到表和字段的注释,为新分区表的表名和字段增加注释

    create or replace procedure p_col_comments_005
    (
      p_tab       in varchar2,
      p_deal_flag in number
    ) as
      /*
      功能:从_YYYYMMDD备份表中得到表和字段的注释,为新分区表的表名和字段增加注释
      */
      v_sql_p_col_comments varchar2(4000);
      v_cnt                number;
    begin
      select count(*)
        into v_cnt
        from user_col_comments
       where table_name = upper(p_tab) || '_' || yyyymmdd
         and comments is not null;
      if v_cnt > 0 then
        for i in (select *
                    from user_col_comments
                   where table_name = upper(p_tab) || '_' || yyyymmdd
                     and comments is not null) loop
          v_sql_p_col_comments := 'comment on column ' || p_tab || '.' ||
                                  i.column_name || ' is ' || '''' || i.comments || '''';
          p_insert_log(p_tab,
                       'p_deal_col_comments',
                       v_sql_p_col_comments,
                       '将新分区表的列的注释加上',
                       5,
                       1);
          p_if_judge(v_sql_p_col_comments, p_deal_flag);
        end loop;
        dbms_output.put_line('操作步骤5(列的注释)-------对' || p_tab || '表增加列名及字段的注释内容');
      else
        dbms_output.put_line('操作步骤5(列的注释)-------' || upper(p_tab) || '_' ||
                             yyyymmdd || '并没有列注释!');
      end if;
    end p_col_comments_005;

    从_YYYYMMDD备份表中得到原表的DEFAULT值,为新分区表的表名和字段增加DEFAULT值

    create or replace procedure p_defau_and_null_006
    (
      p_tab       in varchar2,
      p_deal_flag in number
    ) as
      /*
      功能:从_YYYYMMDD备份表中得到原表的DEFAULT值,为新分区表的表名和字段增加DEFAULT值
      */
      v_sql_defau_and_null varchar2(4000);
      v_cnt                number;
    begin
      select count(*)
        into v_cnt
        from user_tab_columns
       where table_name = upper(p_tab) || '_' || yyyymmdd
         and data_default is not null;
      if v_cnt > 0 then
        for i in (select *
                    from user_tab_columns
                   where table_name = upper(p_tab) || '_' || yyyymmdd
                     and data_default is not null) loop
          v_sql_defau_and_null := 'alter table ' || p_tab || ' modify ' ||
                                  i.column_name || ' default ' ||
                                  i.data_default;
          p_insert_log(p_tab,
                       'p_deal_default',
                       v_sql_defau_and_null,
                       '将新分区表的默认值加上',
                       6);
          p_if_judge(v_sql_defau_and_null, p_deal_flag);
        end loop;
        dbms_output.put_line('操作步骤6(空和默认)-------对' || p_tab ||
                             '表完成默认DEFAULT值的增加');
      else
        dbms_output.put_line('操作步骤6(空和默认)-------' || upper(p_tab) || '_' ||
                             yyyymmdd || '并没有DEFAULT或NULL值!');
      end if;
    end p_defau_and_null_006;

    从_YYYYMMDD备份表中得到原表的CHECK值,为新分区表增加CHECK值

    create or replace procedure p_check_007
    (
      p_tab       in varchar2,
      p_deal_flag in number
    ) as
      /*
      功能:从_YYYYMMDD备份表中得到原表的CHECK值,为新分区表增加CHECK值
      另注:
      user_constraints已经进行了非空的判断,可以略去如下类似的从user_tab_columns获取非空判断的代码编写来判断是否
      for i in (select * from user_tab_columns where table_name=UPPER(P_TAB)||'_' ||YYYYMMDD and nullable='N') loop
      v_sql:='alter table '||p_tab||' modify '||i.COLUMN_NAME ||' not null';
      */
      v_sql_p_check varchar2(4000);
      v_cnt         number;
    begin
      select count(*)
        into v_cnt
        from user_constraints
       where table_name = upper(p_tab) || '_' || yyyymmdd
         and constraint_type = 'C';
      if v_cnt > 0 then
        for i in (select *
                    from user_constraints
                   where table_name = upper(p_tab) || '_' || yyyymmdd
                     and constraint_type = 'C') loop
          v_sql_p_check := 'alter table ' || p_tab || '_' || yyyymmdd ||
                           ' drop constraint ' || i.constraint_name;
          p_insert_log(p_tab,
                       'p_deal_check',
                       v_sql_p_check,
                       '将备份出来的原表的CHECK删除',
                       7,
                       1);
          p_if_judge(v_sql_p_check, p_deal_flag);
          v_sql_p_check := 'alter table ' || p_tab || ' ADD CONSTRAINT ' ||
                           i.constraint_name || ' CHECK (' ||
                           i.search_condition || ')';
          p_insert_log(p_tab,
                       'p_deal_check',
                       v_sql_p_check,
                       '将新分区表的CHECK加上',
                       7,
                       2);
          p_if_judge(v_sql_p_check, p_deal_flag);
        end loop;
        dbms_output.put_line('操作步骤7(check约束)-------对' || p_tab || '完成CHECK的约束');
      else
        dbms_output.put_line('操作步骤7(check约束)-----' || upper(p_tab) || '_' ||
                             yyyymmdd || '并没有CHECK!');
      end if;
    end p_check_007;

    从_YYYYMMDD备份表中得到原表的索引信息,为新分区表增加普通索引(唯一和非唯一索引,函数索引暂不考虑),并删除旧表索引

    create or replace procedure p_index_008
    (
      p_tab            in varchar2,
      p_deal_flag      in number,
      p_idx_tablespace in varchar2
    ) as
      /*
      功能:从_YYYYMMDD备份表中得到原表的索引信息,为新分区表增加普通索引(唯一和非唯一索引,函数索引暂不考虑),并删除旧表索引
      难点:需要考虑联合索引的情况
      */
      v_sql_p_normal_idx varchar2(4000);
      v_cnt              number;
    begin
      select count(*)
        into v_cnt
        from user_indexes
       where table_name = upper(p_tab) || '_' || yyyymmdd
         and index_type = 'NORMAL'
         and index_name not in (select constraint_name from user_constraints);
      if v_cnt > 0 then
        for i in (with t as
                     (select c.*, i.uniqueness
                       from user_ind_columns c,
                            (select distinct index_name, uniqueness
                               from user_indexes
                              where table_name = upper(p_tab) || '_' || yyyymmdd
                                and index_type = 'NORMAL'
                                and index_name not in
                                    (select constraint_name from user_constraints)) i
                      where c.index_name = i.index_name)
                    select index_name,
                           table_name,
                           uniqueness,
                           max(substr(sys_connect_by_path(column_ name, ','), 2)) str ---考虑组合索引的情况
                      from (select column_name,
                                   index_name,
                                   table_name,
                                   row_number() over(partition by index_name, table_name order by column_name) rn,
                                   uniqueness
                              from t) t
                     start with rn = 1
                    connect by rn = prior rn + 1
                           and index_name = prior index_name
                     group by index_name, t.table_name, uniqueness) loop
          v_sql_p_normal_idx := 'drop index ' || i.index_name;
          p_insert_log(p_tab,
                       'p_deal_normal_idx',
                       v_sql_p_normal_idx,
                       '删除原表索引',
                       8,
                       1);
          p_if_judge(v_sql_p_normal_idx, p_deal_flag);
          dbms_output.put_line('操作步骤8(处理索引)-------将' || i.table_name || '' ||
                               i.str || '列的索引' || i.index_name || '删除完毕');
          if i.uniqueness = 'UNIQUE' then
            v_sql_p_normal_idx := 'CREATE UNIQUE INDEX ' || i.index_name ||
                                  ' ON ' || p_tab || '(' || i.str || ')' ||
                                  ' tablespace ' || p_idx_tablespace;
          elsif i.uniqueness = 'NONUNIQUE' then
            v_sql_p_normal_idx := 'CREATE  INDEX ' || i.index_name || ' ON ' ||
                                  p_tab || ' (' || i.str || ')' ||
                                  ' LOCAL tablespace ' || p_idx_tablespace;
          end if;
          p_insert_log(p_tab,
                       'p_deal_normal_idx',
                       v_sql_p_normal_idx,
                       '将新分区表的索引加上',
                       8,
                       2);
          p_if_judge(v_sql_p_normal_idx, p_deal_flag);
          dbms_output.put_line('操作步骤8(处理索引)-------对' || p_tab || '新分区表' ||
                               i.str || '列增加索引' || i.index_name);
        end loop;
      else
        dbms_output.put_line('操作步骤8(处理索引)-------' || upper(p_tab) || '_' ||
                             yyyymmdd || '并没有索引(索引模块并不含主键判断)!');
      end if;
    end p_index_008;

    从_YYYYMMDD备份表中得到原表的主键信息,为新分区表增加主键值,并删除旧表主键

    create or replace procedure p_pk_009
    (
      p_tab            in varchar2,
      p_deal_flag      in number,
      p_idx_tablespace in varchar2
    ) as
      /*
      功能:从_YYYYMMDD备份表中得到原表的主键信息,为新分区表增加主键值,并删除旧表主键
      难点:需要考虑联合主键的情况
      */
      v_sql_p_pk varchar2(4000);
      v_cnt      number;
    
    begin
      select count(*)
        into v_cnt
        from user_ind_columns
       where index_name in (select index_name
                              from sys.user_constraints t
                             where table_name = upper(p_tab) || '_' || yyyymmdd
                               and constraint_type = 'P');
      if v_cnt > 0 then
        for i in (with t as
                     (select index_name, table_name, column_name
                       from user_ind_columns
                      where index_name in
                            (select index_name
                               from sys.user_constraints t
                              where table_name = upper(p_tab) || '_' || yyyymmdd
                                and constraint_type = 'P'))
                    select index_name,
                           table_name,
                           max(substr(sys_connect_by_path(column_name, ','), 2)) str
                      from (select column_name,
                                   index_name,
                                   table_name,
                                   row_number() over(partition by index_name, table_name order by column_name) rn
                              from t) t
                     start with rn = 1
                    connect by rn = prior rn + 1
                           and index_name = prior index_name
                     group by index_name, t.table_name) loop
          v_sql_p_pk := 'alter table ' || i.table_name || ' drop constraint ' ||
                        i.index_name || ' cascade';
          p_insert_log(p_tab,
                       'p_deal_pk',
                       v_sql_p_pk,
                       '将备份出来的原表的主键删除',
                       9,
                       1);
          p_if_judge(v_sql_p_pk, p_deal_flag);
          dbms_output.put_line('操作步骤9(处理主键)-------将备份出来的原表' || i.table_name || '' ||
                               i.str || '列的主键' || i.index_name || '删除完
    毕!'); ---放在FOR循环中效率没问题,因为主键只有一个,只会循环一次
          v_sql_p_pk := 'ALTER TABLE ' || p_tab || ' ADD CONSTRAINT ' ||
                        i.index_name || ' PRIMARY KEY (' || i.str || ')' || ' using index tablespace
    ' || p_idx_tablespace;
          p_insert_log(p_tab,
                       'p_deal_pk',
                       v_sql_p_pk,
                       '将新分区表的主键加上',
                       9,
                       2);
          p_if_judge(v_sql_p_pk, p_deal_flag);
          dbms_output.put_line('操作步骤9(处理主键)-------对' || p_tab || '表的' || i.str ||
                               '列增加主键' || i.index_name); ---放在FOR循环中效率没问题,因为主键只有一个,只会循环一次
        end loop;
      else
        dbms_output.put_line('操作步骤9(处理主键)-------' || upper(p_tab) || '_' ||
                             yyyymmdd || '并没有主键!');
      end if;
    end p_pk_009;

    从_YYYYMMDD备份表中得到原表的外键或主键约束,为新分区表增加外键或主键,并删除旧表的外键或主键

    create or replace procedure p_constraint_010
    (
      p_tab       in varchar2,
      p_deal_flag in number
    ) as
      /*
      功能:从_YYYYMMDD备份表中得到原表的约束,为新分区表增加约束值,并删除旧表约束
      难点:需要考虑联合外键REFERENCE的情况
      */
      v_sql_p_constraint varchar2(4000);
      v_cnt              number;
    begin
      select count(*)
        into v_cnt
        from user_constraints
       where table_name = upper(p_tab) || '_' || yyyymmdd
         and constraint_type = 'R';
      if v_cnt > 0 then
        for i in (with t1 as
                     (select /*+no_merge */
                      position,
                      t.owner,
                      t.constraint_name as constraint_name1,
                      t.table_name      as table_name1,
                      t.column_name     as column_name1
                       from user_cons_columns t
                      where constraint_name in
                            (select constraint_name
                               from user_constraints
                              where table_name = upper(p_tab) || '_' || yyyymmdd
                                and constraint_type = 'R')),
                    t2 as
                     (select /*+no_merge */
                      t.position,
                      c.constraint_name constraint_name1,
                      t.constraint_name as constraint_name2,
                      t.table_name as table_name2,
                      t.column_name as column_name2,
                      max(t.position) over(partition by c.constraint_name) max_position
                       from user_cons_columns t, user_constraints c
                      where c.table_name = upper(p_tab) || '_' || yyyymmdd
                        and t.constraint_name = c.r_constraint_name
                        and c.constraint_type = 'R'),
                    t3 as
                     (select t1.*,
                            t2.constraint_name2,
                            t2.table_name2,
                            t2.column_name2,
                            t2.max_position
                       from t1, t2
                      where t1.constraint_name1 = t2.constraint_name1
                        and t1.position = t2.position)
                    select t3.*,
                           substr(sys_connect_by_path(column_name1, ','), 2) as fk,
                           substr(sys_ connect_by_path(column_name2, ','), 2) as pk
                      from t3
                     where position = max_position
                     start with position = 1
                    connect by constraint_name1 = prior constraint_name1
                           and position = prior position + 1) loop
          v_sql_p_constraint := 'alter table ' || p_tab || '_' || yyyymmdd ||
                                ' drop constraint ' || i.constraint_name1;
          p_insert_log(p_tab,
                       'p_deal_constraint',
                       v_sql_p_constraint,
                       '删除原表FK外键',
                       10,
                       1);
          p_if_judge(v_sql_p_constraint, p_deal_flag);
          dbms_output.put_line('操作步骤10(处理外键)------将备份出来的' || i.table_name1 || '表的' ||
                               i.column_name1 || '列的外键' || i.constraint_name1 ||
                               '删除完毕!');
          v_sql_p_constraint := 'alter table ' || p_tab || ' add constraint ' ||
                                i.constraint_
                                name1 || ' foreign key ( ' || i.fk ||
                                ') references ' || i.table_name2 || ' (' || i.pk || ' )';
          p_insert_log(p_tab,
                       'p_deal_constraint',
                       v_sql_p_constraint,
                       '将新分区表的外键加上',
                       10,
                       2);
          p_if_judge(v_sql_p_constraint, p_deal_flag);
          dbms_output.put_line('操作步骤10(处理外键)------对' || p_tab || '表的' ||
                               i.column_
                               name1 || '列增加外键' || i.constraint_name1);
        end loop;
      else
        dbms_output.put_line('操作步骤10(处理外键)------' || upper(p_tab) || '_' ||
                             yyyymmdd || '并没有外键!');
      end if;
    end p_constraint_010;
  • 相关阅读:
    Java 开源博客 Solo 2.5.0 发布
    redis集群部署及踩过的坑
    开源巨献:年度最佳 JavaScript 和 CSS 开源库推荐!
    Java9 新特性 详解
    eclipse 安装教程
    博客园代码高亮插件(类似csdn的代码插入)
    【超详细教程】使用Windows Live Writer 2012和Office Word 2013 发布文章到博客园全面总结
    软件测试学习视频 分享
    Oracle imp exp 导入导出 执行脚本
    在Ubuntu 15下搭建V/P/N服务器pptpd安装和配置
  • 原文地址:https://www.cnblogs.com/Ryan-Fei/p/14081129.html
Copyright © 2011-2022 走看看