《乾貨分享》分區表改造(腳本模板生成),值得收藏起來實戰再用

前言

過久沒有更新技術博客,後續仍是保持之前的更新速度,走向2020的學習之路,也歡迎你們一塊兒來學習學習。最後撈一下之前發的面試文章總結,後續將繼續更新:java

  • 1、分區表簡介

    1.1 什麼是分區表?

    分區表是將大表的數據分紅稱爲分區的許多小的子集,分區表的種類劃分主要有:range(範圍)、list(列表)和hash(散列)分區。劃分依據主要是根據其表內部屬性。程序員

    分區表能夠建立其獨特的分區索引,分區表能夠從物理上將一個大表分紅幾個小表,可是從邏輯上來看,仍是一個大表。

    1.2 什麼狀況下使用分區表呢?

    表內的數據量很大的時候,影響到業務/技術方容忍的最大查詢時間。但數據量並非判斷是否須要建立分區表的唯一條件,若是表內的數據都是基礎數據、其數據查詢都頻率高,這樣不建議使用分區表。一般狀況下,能夠將數據進行分段處理。面試

    表的大小超過2GB可進去分區表改造

    1.3 爲何使用分區表

    1. 改善查詢性能:對分區對象的查詢能夠僅搜索本身關心的分區,提升檢索速度。
    2. 加強可用性:若是表的某個分區出現故障,表在其餘分區的數據仍然可用;
    3. 維護方便:若是表的某個分區出現故障,須要修復數據,只修復該分區便可;
    4. 均衡I/O:能夠把不一樣的分區映射到磁盤以平衡I/O,改善整個系統性能。

    1.4 表分區的類型

    範圍分區(range):基於一個範圍將表的數據分配到其所屬的分區內。算法

    若是須要將行映射到基於列值範圍的分區時,就使用範圍分區方法--條件是數據能夠被劃分紅邏輯範圍;當數據在整個範圍內能被均等地劃分時性能最好,明顯不能均分時須使用其餘分區方式

    「範圍」是在建立分區表時指定的分區鍵決定的,分區方式是最爲經常使用的,而且分區鍵常常採用日期sql

    列表分區(list):基於列某個特性分配其所屬的分區數據庫

    該分區的特色是某列的值只有幾個,基於這樣的特色咱們能夠採用列表分區

    散列分區(hash):在列值上使用散列算法來分配其所屬分區segmentfault

    當列的值沒有合適的條件時,建議使用散列分區,經過在I/O設備上進行散列分區,使得這些分區大小一致。

    2、分區表改造方案

    此次主要討論的是以範圍分區(range),而且以日期做爲分區鍵。數組

    2.1 分區改造前準備

    在作表分區前,須要表統計分析,各個表、索引空間存儲大小,每一年或者每月表的增加率等(能夠找DBA)。這邊提供DBA經常使用系統表,視圖——https://docs.oracle.com/cd/B1...性能優化

    2.1.1 統計各個表空間大小

    select t.owner,t.segment_name,t.tablespace_name,sum(bytes/1024/1024/1024) gb
    from dba_segments t 
    where t.segment_name in (select t2.OBJECT_NAME
                              from dba_objects t2
                             where t2.OBJECT_TYPE = 'TABLE'
                             AND t2.owner=upper('tabel_owner')
                             )
    group by t.owner,t.segment_name,t.tablespace_name
    order by 4 desc;

    2.1.2 統計表的索引大小

    select round(sum(bytes) / 1024 / 1024 / 1024, 4) IDX_GB  --表上索引對象佔用空間
              from dba_segments
             where owner || segment_name in(select owner || index_name
                      from dba_indexes
                     where table_owner = upper('table_name')
                       and table_name = upper('table_owner'));

    2.2分區表改造步驟

    前面表的各項指標都分析統計出來,那就開始實際操做起來,首先進行的小於100G的改造方案:多線程

    1. 建立與原表同構的分區新表
    2. 將原表設置成read only
    3. 使用Insert..select from的方式,將原表數據導入到分區新表
    4. 建立分區新表的索引和約束
    5. rename源表和新表的索引名和約束名稱,交換命名
    6. 刪除源表的同義詞
    7. rename源表和分區新表的表名,交換命名
    8. 建立源表和新表的同義詞
    9. 給rename後的分區新表受權
    10. 將rename後的源表設置爲read write.
    Note:在進行接下來的腳本的時候記得定義好所需的變量

    declare
    v_table_name varchar2(100) := upper('表名');
    ----建表變量
    v_sql_temp1 varchar2(1000);
    v_sql_temp2 varchar2(1000);
    v_sql_temp3 varchar2(1000);
    ----屬主變量
    v_owner varchar2(100) := upper('表屬主');
    -----輸出變量
    type remark_list is varray(60) of varchar2(3000);
    v_output_list remark_list;
    -----受權變量
    v_grantee varchar2(100);
    v_grant_sql varchar2(1000);
    type type_array is table of varchar(20) index by binary_integer;
    grantee_list type_array;

    2.2.1 建立與原表同構的分區新表

    步驟以下:

    1. 建立分區表
    2. 添加字段的默認值
    3. 添加表以及字段的註釋
    建立分區表

    建立分區表--腳本生成模板1:

    羅列全部字段模板,涉及到dba_tab_columns(表的列信息)

    -----------1.新建分區臨時表
      
      v_sql_temp1   := 'CREATE TABLE ' || v_owner || '.' || v_table_name ||
                       '_P( ';
               
     select COLUMN_NAME ||'   '|| decode( DATA_TYPE,'DATE',DATA_TYPE, DATA_TYPE||'('|| DATA_LENGTH || ')')
     || decode(NULLABLE,'N','  not null','') || ',' bulk collect
        into v_output_list
      from   dba_tab_columns t 
      where  table_name =v_table_name
      order by t.COLUMN_ID ;  
    
      dbms_output.put_line(v_sql_temp1);
    
      for i in 1 ..v_output_list.count  loop
        --去掉數組中的最後一個字段字符中的逗號","
        if i = v_output_list.count then
         select  REPLACE(v_output_list(i),',',' ') into v_sql_temp2 from dual;
         dbms_output.put_line(v_sql_temp2);
       else
         dbms_output.put_line(v_output_list(i));
       end if;
    
      end loop;
      --輸出分區信息
      v_sql_temp3 := ')PARTITION BY RANGE (ARCHIVE_DATE) INTERVAL (NUMTOYMINTERVAL(1, ''YEAR'')) (PARTITION ' || v_table_name || '_2019' ||
                       ' VALUES LESS THAN (TO_DATE(''2020-01-01'', ''YYYY-MM-DD'')))ENABLE ROW MOVEMENT MONITORING INITRANS 6;';
      dbms_output.put_line(v_sql_temp3);

    生成的模板:

    CREATE TABLE PASDATA.CLM_PERSON_HOSPITAL_P( 
        CREATED_BY   VARCHAR2(100)  not null,
        CREATED_DATE   DATE  not null,
        UPDATED_BY   VARCHAR2(100)  not null,
        UPDATED_DATE   DATE  not null,
        ID_CLM_PERSON_HOSPITAL   VARCHAR2(32)  not null,
        REPORT_NO   VARCHAR2(30)  not null,
        ID_CLM_CHANNEL_PROCESS   VARCHAR2(32)  not null,
        CASE_TIMES   NUMBER(22)  not null,
        HOSPITAL_CODE   VARCHAR2(22),
        HOSPITAL_NAME   VARCHAR2(100),
        SUBJECT_CODE   VARCHAR2(20),
        BED_CODE   VARCHAR2(100),
        START_DATE   DATE,
        END_DATE   DATE,
        MIGRATE_FROM   VARCHAR2(1),
        AFFIRM_SIGN   VARCHAR2(1)  not null,
        DOCUMENT_GROUP_ID   VARCHAR2(30),
        HOSPITALIZATION_NUMBER   VARCHAR2(30),
        HOSPITALIZE_DAYS   NUMBER(22),
        ARCHIVE_DATE   DATE 
    )PARTITION BY RANGE (ARCHIVE_DATE) INTERVAL (NUMTOYMINTERVAL(1, 'YEAR')) (
     PARTITION CLM_PERSON_HOSPITAL_2018 VALUES LESS THAN (TO_DATE('2019-01-01', 'YYYY-MM-DD'))
    )ENABLE ROW MOVEMENT MONITORING INITRANS 6;

    建立分區表--腳本生成模板2:

    declare
      v_table_name varchar2(100) := upper('表名');
      -------建表變量
      v_sql_temp1   varchar2(1000);
      v_sql_temp2   varchar2(1000);
      v_sql_partion varchar2(1000);
      ----屬主變量
      v_owner varchar2(100) := upper('表屬主');
      -----輸出變量 若是字段數量超過60個,修改數組大小便可
      type remark_list is varray(60) of varchar2(3000);
      v_remark_list remark_list;
      
    begin
      ----在begin後面加上DBMS_OUTPUT.ENABLE(buffer_size => null) ,表示輸出buffer不受限制。
      DBMS_OUTPUT.ENABLE(buffer_size => null);
      ----------------------------------------1.新建分區臨時表----------------------------------
      v_sql_temp1   := 'CREATE TABLE ' || v_owner || '.' || v_table_name ||
                       '_P PARTITION BY RANGE (ARCHIVE_DATE) INTERVAL (NUMTOYMINTERVAL(1, ''YEAR'')) (';
      v_sql_temp2 := 'PARTITION ' || v_table_name || '_2019' ||
                       ' VALUES LESS THAN (TO_DATE(''2020-01-01'', ''YYYY-MM-DD''))';
      v_sql_temp3   := ')ENABLE ROW MOVEMENT MONITORING INITRANS 6 AS SELECT * FROM  ' ||
                       v_owner || '.' || v_table_name || '  WHERE 1=0;';
      dbms_output.put_line(v_sql_temp1 || v_sql_temp2 || v_sql_temp3);

    生成的模板:

    CREATE TABLE PASDATA.EDR_APPLY_PLAN_INFO_P 
    PARTITION BY RANGE (ARCHIVE_DATE) INTERVAL (NUMTOYMINTERVAL(1, 'YEAR')) (
    PARTITION PART_BEFORE_2018 VALUES LESS THAN (TO_DATE('2018-01-01', 'YYYY-MM-DD'))
    )ENABLE ROW MOVEMENT MONITORING INITRANS 6 
    AS 
    SELECT * FROM  PASDATA.EDR_APPLY_PLAN_INFO  WHERE 1=0;
    添加分區表字段的默認值
    ------- 2.對分區新表字段增長默認值
      select 'alter table ' || t.owner || '.' || t.table_name || '_NEW modify ' ||
             t.column_name || ' default  記得填默認值;' bulk collect
        into v_output_list
        from DBA_TAB_COLS t
       where t.TABLE_NAME = v_table_name
         and t.owner = v_owner
         and t.data_default is not null;
    
      for i in 1 .. v_output_list.count loop
        dbms_output.put_line(v_output_list(i));
      end loop;
    添加表以及字段的註釋

    添加表以及字段的註釋涉及到表 dba_tab_comments(表註釋信息)、dba_col_comments(列註釋信息)

    ------------- 3.對分區新表的表名,字段增長註釋
    
      select 'comment on table ' || a.owner || '.' || a.table_name || '_P is ''' ||
             a.comments || ''';' bulk collect
        into v_output_list
        from dba_tab_comments a
       where a.table_name = upper(v_table_name)
         and a.owner = upper(v_owner);
      for i in 1 .. v_output_list.count loop
        dbms_output.put_line(v_output_list(i));
      end loop;
    
      select 'comment on column ' || owner || '.' || table_name || '_P.' ||
             column_name || ' is ' || '''' || comments || ''';' bulk collect
        into v_output_list
        from dba_col_comments
       where table_name = v_table_name;
    
      for i in 1 .. v_output_list.count loop
        dbms_output.put_line(v_output_list(i));
      end loop;

    2.2.3 使用Insert..select from的方式,將原表數據導入到分區新表

    dbms_output.put_line('insert /*+ append parallel(A, 4) */ into ' ||
                           v_owner || '.' || v_table_name ||
                           '_P A select /*+ parallel(T, 4) */  * from ' ||
                           v_owner || '.' || v_table_name || ' T;');
      dbms_output.put_line('commit;');

    2.2.4 建立分區新表的索引和約束

    建立分區表索引

    建立分區表索引涉及到dba_indexes (用戶模式的索引信息)、dba_ind_columns( 索引與表字段的相關信息)

    select 'create ' || decode(a.uniqueness, 'UNIQUE', 'UNIQUE', '') ||
             ' index ' || a.owner || '.' || a.index_name || '_N on ' ||
             a.table_owner || '.' || a.table_name || '_P (' ||
             (select wm_concat(b.column_name)
                from dba_ind_columns b
               where b.index_name = a.index_name
                 and b.table_owner = v_owner) || ') initrans 16 PARALLEL 8 online;' bulk collect
        into v_output_list
        from dba_indexes a
       where a.table_name = v_table_name
         and a.index_type != 'LOB';
         
      for i in 1 .. v_output_list.count loop
       dbms_output.put_line(v_output_list(i));
      end loop;
    建立表約束

    建立分區表索約束涉及到dba_cons_columns(數據庫全部列的約束信息)、dba_constraints( 數據庫中全部表的全部約束定義),當dba_constraints中的constraint_type值爲爲p時爲表主鍵,值爲R時爲外鍵。

    1. 建立主鍵約束

      ----建立主鍵約束
        select 'ALTER TABLE ' || a.owner || '.' || a.table_name ||
               '_P ADD CONSTRAINT ' || a.constraint_name || '_N PRIMARY KEY (' ||
               a.column_name || ');' bulk collect
          into v_output_list
          from dba_cons_columns a
         where a.constraint_name =
               (select constraint_name
                  from dba_constraints b
                 where b.table_name = v_table_name
                 and b.owner = a.owner
                  and constraint_type = 'P')
                  and a.owner = v_owner;
        for i in 1 .. v_output_list.count loop
          dbms_output.put_line(v_output_list(i));
        end loop;
    2. 建立外鍵約束

      -----(若是有外鍵的話,建立外鍵約束)
        select 'alter table ' || a.owner || '.' || a.table_name ||
               '_P add constraint ' || a.constraint_name || '_N foreign key(' ||
               b.column_name || ') references ' ||
               (select c.owner || '.' || c.table_name || '(' || c.column_name || ')'
                  from dba_cons_columns c, dba_constraints d
                 where c.constraint_name = d.constraint_name
                   and d.constraint_type = 'P'
                   and c.constraint_name = a.r_constraint_name
                   and c.owner = v_owner
                   and d.owner = v_owner) || ';' bulk collect
          into v_output_list
          from dba_constraints a, dba_cons_columns b
         where a.constraint_name = b.constraint_name
           and a.table_name = v_table_name
           and a.constraint_type = 'R';
       for i in 1 .. v_output_list.count loop
          dbms_output.put_line(v_output_list(i));
        end loop;

    2.2.5 rename源表和新表的索引名和約束名稱以及表名

    將原表索引、表約束變動爲臨時索引,約束
    select 'alter index ' || a.owner || '.' || a.index_name || ' rename to ' ||
             a.index_name || '_T;' bulk collect
        into v_output_list
        from dba_indexes a
       where a.table_name = v_table_name
         and a.index_type != 'LOB';
    
      for i in 1 .. v_output_list.count loop
        dbms_output.put_line(v_output_list(i));
      end loop;
      
    select 'alter table ' || a.owner || '.' || a.table_name ||
             ' rename constraint ' || a.constraint_name || ' to ' ||
             a.constraint_name || '_T;' bulk collect
       into v_output_list
       from dba_constraints a
      where a.table_name = v_table_name
        and a.constraint_type in ('P', 'R');
    
     for i in 1 .. v_output_list.count loop
        dbms_output.put_line(v_output_list(i));
     end loop;
    將分區表的索引名、表約束變動爲原表的索引、約束
    select 'alter index ' || a.owner || '.' || a.index_name ||
             '_N rename to ' || a.index_name || ';' bulk collect
        into v_output_list
        from dba_indexes a
       where a.table_name = v_table_name
         and a.index_type != 'LOB';
      for i in 1 .. v_output_list.count loop
        dbms_output.put_line(v_output_list(i));
      end loop;
    
      select 'alter table ' || a.owner || '.' || a.table_name ||
             '_P rename constraint ' || a.constraint_name || '_N to ' ||
             a.constraint_name || ';' bulk collect
        into v_output_list
        from dba_constraints a
       where a.table_name = v_table_name
         and a.constraint_type in ('P', 'R');
      for i in 1 .. v_output_list.count loop
        dbms_output.put_line(v_output_list(i));
      end loop;

    2.2.6 給rename後的分區新表受權

    給rename後的分區新表受權涉及到dba_tab_privs(數據庫全部列的受權信息),查詢全部的受權列表進行輸出,定義好grantee_list、v_grant_sql等變量

    select distinct (t.grantee) bulk collect
        into grantee_list
        from dba_tab_privs t
       where (t.owner = upper(v_owner))
         and t.table_name = v_table_name;
      for i in 1 .. grantee_list.count loop
        v_grantee := grantee_list(i);
    
    
    select 'grant ' || (select wm_concat(t.privilege)
                          from dba_tab_privs t
                         where t.table_name = v_table_name
                           and t.grantee = v_grantee) || ' on ' || t.owner || '.' ||
           t.table_name || ' to ' || t.grantee || ';'
      into v_grant_sql
      from dba_tab_privs t
     where t.table_name = v_table_name
       and t.grantee = v_grantee
       and rownum = 1;
    dbms_output.put_line(v_grant_sql);
    end loop;

    2.3 分區表改造完成

    當分區表的改造完成後保險地進行驗證一下,數據量,索引,受權列表

    對比索引

    select * from  dba_indexes a where a.table_name = 'CLM_HIS_RECIPE_DETAIL_NEW'
         and a.index_type != 'LOB';

    對比受權用戶列表

    select *
      from dba_tab_privs t
     where t.table_name in
           ('CLM_PERSON_HOSPITAL_NEW')
       and t.owner = 'CLAIMDATA';
    各位看官還能夠嗎?喜歡的話,動動手指點個💗,點個關注唄!!謝謝支持!

    歡迎關注公衆號【Ccww技術博客】,原創技術文章第一時間推出

    image.png

    相關文章
    相關標籤/搜索