線上重定義的操作步驟__普通錶轉換為分割槽表的實驗案例

lusklusklusk發表於2018-06-14
普通錶轉換為分割槽表的線上重定義的步驟
(1)確定表A是否可以線上重定義
  表A有主鍵的情況下

  DBMS_REDEFINITION.CAN_REDEF_TABLE('使用者名稱','A表名') 
  或
  表A沒有主鍵的情況下

  DBMS_REDEFINITION.CAN_REDEF_TABLE('使用者名稱','A表名',options_flag => DBMS_REDEFINITION.CONS_USE_ROWID)

(2)建立臨時的分割槽表B

(3)開始重定義,此過程會鎖表,此時select可以查詢到B表有了資料
  表A有主鍵的情況下

  DBMS_REDEFINITION.start_redef_table('使用者名稱','A表名','B表名')
  或
  表A沒有主鍵的情況下

  DBMS_REDEFINITION.start_redef_table('使用者名稱','A表名','B表名',options_flag => DBMS_REDEFINITION.CONS_USE_ROWID)

(4)將表A的依賴約束、觸發器、索引等依賴物件同步到臨時分割槽表B,此過程不會鎖表,此時select可以查到B表有了這些約束、觸發器、索引den依賴物件
  DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS

(5)將表A的資料同步到臨時分割槽表B,此過程不會鎖表,此動作可以做多次,只是同步重定義過程中DML產生的資料到表B
  DBMS_REDEFINITION.sync_interim_table('使用者名稱','A表名','B表名')
 在透過呼叫START_REDEF_TABLE啟動重新定義過程之後,在呼叫FINISH_REDEF_TABLE之前,可能在原始表上執行了大量DML語句。如果您知道確實如此,建議您定期將臨時表與原始表同步。這是透過呼叫SYNC_INTERIM_TABLE過程完成的。呼叫此過程可以減少FINISH_REDEF_TABLE完成重定義過程所需的時間。呼叫SYNC_INTERIM_TABLE的次數沒有限制。

(6)結束重定義,此過程會鎖表,結束後才將2個表進行了交換,在此之前的3、4、5步過程中兩表維持原來定義沒變(表A還是普通表,表B還是分割槽表)重定義結束後基表A成了分割槽表,我們建立的臨時分割槽表B成了普通表。此時我們可以刪除我們建立的臨時表B。
  DBMS_REDEFINITION.finish_redef_table('使用者名稱','A表名','B表名')
  如果表A沒有主鍵使用rowid,最後再執行ALTER TABLE 表A DROP UNUSED COLUMNS





線上重定義的限制
1、重新定義具有物化檢視日誌的表之後,任何依賴物化檢視的後續重新整理必須是完全重新整理
2、索引組織表不能線上重新定義
3、具有細粒度訪問控制(行級安全性)的表不能線上重新定義
4、已啟用Flashback資料存檔的表不能線上重新定義
5、SYS和SYSTEM中的表不能線上重新定義
6、臨時表不能線上重新定義
7、分割槽的表如果有一個或多個巢狀表,則不能線上重新定義



表A有主鍵的案例
create table test as select * from dba_objects
update test set CREATED=sysdate-(365*8) where object_id between 0 and 10000;
update test set CREATED=sysdate-(365*7) where object_id between 10000 and 20000;
update test set CREATED=sysdate-(365*6) where object_id between 20000 and 30000;
update test set CREATED=sysdate-(365*5) where object_id between 30000 and 40000;
update test set CREATED=sysdate-(365*4) where object_id between 40000 and 50000;
update test set CREATED=sysdate-(365*3) where object_id between 50000 and 60000;
update test set CREATED=sysdate-(365*2) where object_id between 60000 and 70000;
update test set CREATED=sysdate-(365*1) where object_id between 70000 and 80000;
alter table test add constraint IND_OBJ primary key  ( OBJECT_ID );
create index IND_CREATED on  test(created)

SQL> select partitioned from user_tables where table_name='TEST';
PAR
---
NO

SQL> select count(*) from test where CREATED between sysdate-(365*9) and sysdate-(365*8)
  2  union all
  3  select count(*) from test where CREATED between sysdate-(365*8) and sysdate-(365*7)
  4  union all
  5  select count(*) from test where CREATED between sysdate-(365*7) and sysdate-(365*6)
  6  union all
  7  select count(*) from test where CREATED between sysdate-(365*6) and sysdate-(365*5)
  8  union all
  9  select count(*) from test where CREATED between sysdate-(365*5) and sysdate-(365*4)
 10  union all
 11  select count(*) from test where CREATED between sysdate-(365*4) and sysdate-(365*3)
 12  union all
 13  select count(*) from test where CREATED between sysdate-(365*3) and sysdate-(365*2)
 14  union all
 15  select count(*) from test where CREATED between sysdate-(365*2) and sysdate-(365*1)
 16  union all
 17  select count(*) from test where CREATED between sysdate-(365*1) and sysdate-(365*0);
COUNT(*)
----------
    9829
    9848
    10000
    10000
    10000
    9959
    9536
    6077
    0

SQL> select index_name,column_name from user_ind_columns where table_name='TEST';
INDEX_NAME           COLUMN_NAME
-------------------- --------------------
IND_OBJ              OBJECT_ID
IND_CREATED          CREATED

SQL> select constraint_name,constraint_type from user_constraints where table_name='TEST';
CONSTRAINT_NAME                C
------------------------------ -
IND_OBJ                        P

SQL> select to_char(max(created),'yyyy-mm-dd hh24:mi:ss') from test where created<sysdate-(365*8);
TO_CHAR(MAX(CREATED
-------------------
2010-06-14 23:50:24

建立臨時的分割槽表T1
SQL> CREATE TABLE T1
(
  OWNER           VARCHAR2(30 BYTE),
  OBJECT_NAME     VARCHAR2(128 BYTE),
  SUBOBJECT_NAME  VARCHAR2(30 BYTE),
  OBJECT_ID       NUMBER,
  DATA_OBJECT_ID  NUMBER,
  OBJECT_TYPE     VARCHAR2(19 BYTE),
  CREATED         DATE,
  LAST_DDL_TIME   DATE,
  TIMESTAMP       VARCHAR2(19 BYTE),
  STATUS          VARCHAR2(7 BYTE),
  TEMPORARY       VARCHAR2(1 BYTE),
  GENERATED       VARCHAR2(1 BYTE),
  SECONDARY       VARCHAR2(1 BYTE),
  NAMESPACE       NUMBER,
  EDITION_NAME    VARCHAR2(30 BYTE)
)
PARTITION BY RANGE(CREATED) INTERVAL (numtoyminterval(1,'YEAR'))
( PARTITION p01 VALUES LESS THAN (to_date('2010-06-14 23:50:25','yyyy-mm-dd hh24:mi:ss')));

確定表TEST是否可以線上重定義
會話1,不提交
update test set object_id=object_id+1 where object_id in (select max(object_id) from test);
SQL> select max(object_id) from test;
MAX(OBJECT_ID)
--------------
77001

會話2
SQL> exec DBMS_REDEFINITION.CAN_REDEF_TABLE('hr','test');
PL/SQL procedure successfully completed.
會話1不提交,CAN_REDEF_TABLE正常執行
SQL> select max(object_id) from test;
MAX(OBJECT_ID)
--------------
         77000

開始重定義
會話2
SQL> begin
  2  DBMS_REDEFINITION.start_redef_table('hr','test','t1');
  3  end;
  4  /
會話1不提交,start_redef_table會一直等待,可以透過v$session查詢到堵塞事件是enq: TX - row lock contention。

會話1 commit;
會話2 PL/SQL procedure successfully completed.

將TEST的依賴物件同步到T1
會話1,不提交
update test set object_id=object_id+1 where object_id in (select max(object_id) from test);
SQL> select max(object_id) from test;
MAX(OBJECT_ID)
--------------
77002

會話2
DECLARE
  2  num_errors PLS_INTEGER;
  3  begin
  4  DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS('hr','test','t1',DBMS_REDEFINITION.CONS_ORIG_PARAMS, TRUE, TRUE, TRUE, TRUE, num_errors);
  5  end;
  6  /
PL/SQL procedure successfully completed.
會話1不提交,COPY_TABLE_DEPENDENTS可以正常執行

將TEST的資料同步到T1
SQL> begin
  2  dbms_redefinition.sync_interim_table('hr','test','t1');
  3  end;
  4  /
PL/SQL procedure successfully completed.
會話1不提交,sync_interim_table可以正常執行

結束重定義
SQL> begin
  2  dbms_redefinition.finish_redef_table('hr','test','t1');
  3  end;
  4  /
會話1不提交,則上面的finish_redef_table會一直等待,但是v$session查不到堵塞,v$lock可以查到會話1和會話2都持有鎖,但是都不請求對方


驗證
SQL> select index_name,column_name from user_ind_columns where table_name='TEST';
INDEX_NAME           COLUMN_NAME
-------------------- --------------------
IND_OBJ              OBJECT_ID
IND_CREATED          CREATED

SQL> select constraint_name,constraint_type from user_constraints where table_name='TEST';
CONSTRAINT_NAME                C
------------------------------ -
IND_OBJ                        P

SQL> select partitioned from user_tables where table_name='TEST';
PAR
---
YES

select * from user_tab_partitions
insert into test (created) values(sysdate+699)--因為建表B時使用了interver,所以表A也發現自動擴充套件了一個分割槽







表A沒有主鍵的案例
drop table test purge
drop table t1 purge
create table test as select * from dba_objects;
update test set CREATED=sysdate-(365*8) where object_id between 0 and 10000;
update test set CREATED=sysdate-(365*7) where object_id between 10000 and 20000;
update test set CREATED=sysdate-(365*6) where object_id between 20000 and 30000;
update test set CREATED=sysdate-(365*5) where object_id between 30000 and 40000;
update test set CREATED=sysdate-(365*4) where object_id between 40000 and 50000;
update test set CREATED=sysdate-(365*3) where object_id between 50000 and 60000;
update test set CREATED=sysdate-(365*2) where object_id between 60000 and 70000;
update test set CREATED=sysdate-(365*1) where object_id between 70000 and 80000;
create index IND_CREATED on  test(created)

SQL> select partitioned from user_tables where table_name='TEST';
PAR
---
NO

SQL> select index_name,column_name from user_ind_columns where table_name='TEST';
INDEX_NAME           COLUMN_NAME
-------------------- --------------------
IND_CREATED          CREATED

SQL> select constraint_name,constraint_type from user_constraints where table_name='TEST';
no rows selected

SQL> select to_char(max(created),'yyyy-mm-dd hh24:mi:ss') from test where created<sysdate-(365*8);
TO_CHAR(MAX(CREATED
-------------------
2010-06-14 23:50:24

建立臨時的分割槽表T1
SQL> CREATE TABLE T1
(
  OWNER           VARCHAR2(30 BYTE),
  OBJECT_NAME     VARCHAR2(128 BYTE),
  SUBOBJECT_NAME  VARCHAR2(30 BYTE),
  OBJECT_ID       NUMBER,
  DATA_OBJECT_ID  NUMBER,
  OBJECT_TYPE     VARCHAR2(19 BYTE),
  CREATED         DATE,
  LAST_DDL_TIME   DATE,
  TIMESTAMP       VARCHAR2(19 BYTE),
  STATUS          VARCHAR2(7 BYTE),
  TEMPORARY       VARCHAR2(1 BYTE),
  GENERATED       VARCHAR2(1 BYTE),
  SECONDARY       VARCHAR2(1 BYTE),
  NAMESPACE       NUMBER,
  EDITION_NAME    VARCHAR2(30 BYTE)
)
PARTITION BY RANGE(CREATED) INTERVAL (numtoyminterval(1,'YEAR'))
( PARTITION p01 VALUES LESS THAN (to_date('2010-06-14 23:50:25','yyyy-mm-dd hh24:mi:ss')));

確定表TEST是否可以線上重定義
會話1,不提交
update test set object_id=object_id+1 where object_id in (select max(object_id) from test);
SQL> select max(object_id) from test;
MAX(OBJECT_ID)
--------------
77001

會話2
SQL>  exec DBMS_REDEFINITION.CAN_REDEF_TABLE('hr','test',options_flag => DBMS_REDEFINITION.CONS_USE_ROWID);
PL/SQL procedure successfully completed.
會話1不提交,CAN_REDEF_TABLE正常執行
SQL> select max(object_id) from test;
MAX(OBJECT_ID)
--------------
         77000

開始重定義
SQL> begin
  2  DBMS_REDEFINITION.start_redef_table('hr','test','t1',options_flag => DBMS_REDEFINITION.CONS_USE_ROWID);
  3  end;
  4  /
會話1不提交,start_redef_table會一直等待,可以透過v$session查詢到堵塞事件是enq: TX - row lock contention。

會話1 commit;
會話2 PL/SQL procedure successfully completed.

將TEST的依賴物件同步到T1
會話1,不提交
update test set object_id=object_id+1 where object_id in (select max(object_id) from test);
SQL> select max(object_id) from test;
MAX(OBJECT_ID)
--------------
77002

會話2
DECLARE
  2  num_errors PLS_INTEGER;
  3  begin
  4  DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS('hr','test','t1',DBMS_REDEFINITION.CONS_ORIG_PARAMS, TRUE, TRUE, TRUE, TRUE, num_errors);
  5  end;
  6  /
PL/SQL procedure successfully completed.
會話1不提交,COPY_TABLE_DEPENDENTS可以正常執行

將TEST的資料同步到T1
SQL> begin
  2  dbms_redefinition.sync_interim_table('hr','test','t1');
  3  end;
  4  /
PL/SQL procedure successfully completed.
會話1不提交,sync_interim_table可以正常執行

結束重定義
SQL> begin
  2  dbms_redefinition.finish_redef_table('hr','test','t1');
  3  end;
  4  /
會話1不提交,則上面的finish_redef_table會一直等待,但是v$session查不到堵塞,v$lock可以查到會話1和會話2都持有鎖,但是都不請求對方


驗證
SQL> select index_name,column_name from user_ind_columns where table_name='TEST';
INDEX_NAME           COLUMN_NAME
-------------------- --------------------
IND_CREATED          CREATED

SQL> select constraint_name,constraint_type from user_constraints where table_name='TEST';
no rows selected

SQL> select partitioned from user_tables where table_name='TEST';
PAR
---
YES

select * from user_tab_partitions
insert into test (created) values(sysdate+699)--因為建表B時使用了interver,所以表A也發現自動擴充套件了一個分割槽 
select * from user_tab_cols where table_name='TEST'--發現有個隱藏欄位
ALTER TABLE TEST DROP UNUSED COLUMNS--刪除該隱藏欄位

Choose the redefinition method: by key or by rowid

By key
—Select a primary key or pseudo-primary key to use for the redefinition. Pseudo-primary keys are unique keys with all component columns having NOT NULLconstraints. For this method, the versions of the tables before and after redefinition should have the same primary key columns. This is the preferred and default method of redefinition.

By rowid—Use this method if no key is available. In this method, a hidden column named M_ROW$$ is added to the post-redefined version of the table. It is recommended that this column be dropped or marked as unused after the redefinition is complete. If COMPATIBLE is set to 10.2.0 or higher, the final phase of redefinition automatically sets this column unused. You can then use the ALTER TABLE ... DROP UNUSED COLUMNS statement to drop it.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30126024/viewspace-2156145/,如需轉載,請註明出處,否則將追究法律責任。

相關文章