ORACLE系统包DBMS_REDEFINITION实现表的在线重定义

本文介绍如何使用Oracle的DBMS_REDEFINITION包实现表的在线重定义,包括普通表到分区表的转换过程及注意事项。

        ORACLE自带的DBMS_REDEFINITION包功能非常强大,可以用于降低高水位线,回收碎片空间,对表做在线重定义(添加或删除字段,改变字段类型,普通表重定义表为分区表,分区表重定义表为普通表等等)

        另外,它有一个非常强大的功能,“在线”二字体现得淋漓尽致,但是也并不是完全的在线,因为在完成重定义前的最后一下,会持会表级排他锁,但这个锁定时间是可控的。

        如果你也曾为delete无法降低高水位线而烦恼或者由于历史表太大导致维护困难,我相信这篇文章将对你有很大的感触,因此相信绝大朋友都用过诸如exp/imp,EXPDP/IMPDP,SHINK SPACE,MOVE等释放碎片,但业务影响范围非常大,相比之下DBMS_REDEFINITION的方式就有太多的优势了!

 

以下,拿普通表在线重定义成分区表为例,演示历史表过大导致维护困难:

 

SQL> list
  1* create table ori_tab tablespace users as select * from ab
SQL> select count(*) from ori_tab;

  COUNT(*)
----------
       100

SQL> SQL> ed
Wrote file afiedt.buf

  1  begin
  2    dbms_stats.gather_table_stats( 'T1','ORI_TAB' );
  3* end;
SQL> /

PL/SQL procedure successfully completed.
SQL> select blocks,empty_blocks from user_tables where table_name='ORI_TAB';

    BLOCKS EMPTY_BLOCKS
---------- ------------
  4       0               <<<<占用4个数据块

SQL>  delete from ori_tab where a > 50;

50 rows deleted.

SQL> COMMIT;

Commit complete.

SQL> select count(distinct dbms_rowid.rowid_block_number(rowid)) from ori_tab;

COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
        1        <<<<<<<<<实际数据只占用1个数据块,说明delete操作并没有回收高水位线

SQL> alter table ori_tab add constraint oritab_a_pk primary key (a);    ---为表创建主键,为了在线重定做准备(重定义可以有两种方式rowid或主键_not NULL)

Table altered.


+++1检查ORI_TAB是否支持在线重定义
SQL> exec dbms_redefinition.can_redef_table(uname => 'T1',tname => 'ORI_TAB');

PL/SQL procedure successfully completed.

没有报错,说明当前表支持在线重定义

+++2创建新表INT_TAB
SQL> ed
Wrote file afiedt.buf

  1  CREATE TABLE int_tab
  2  ( a number
  3  , b varchar2(2)
  4  )
  5    PARTITION BY RANGE (a)
  6  ( PARTITION p0 VALUES LESS THAN (10),
  7    PARTITION p1 VALUES LESS THAN (20),
  8    PARTITION p2 VALUES LESS THAN (30),
  9  PARTITION p3 VALUES LESS THAN (40),
 10*  PARTITION p4 values less than (maxvalue))
SQL> /

Table created.


+++3 开始做
SQL> alter session force parallel dml parallel 4;

Session altered.

SQL> alter session force parallel query parallel 4;

Session altered.

SQL> exec dbms_redefinition.start_redef_table(uname => 'T1',orig_table => 'ORI_TAB',int_table => 'INT_TAB',col_mapping => NULL,options_flag => DBMS_REDEFINITION.CONS_USE_PK);

PL/SQL procedure successfully completed.

SQL> SELECT COUNT(*) FROM INT_TAB;

  COUNT(*)
----------
 50       <<<<<<<已经看到数据被复制过去了

SQL> select index_name from user_indexes where table_name='INT_TAB';

no rows selected               <<<<<<<<<<<<索引还未自动创建

SQL> change/INT_TAB/ORI_TAB
  1* select index_name from user_indexes where table_name='ORI_TAB'
SQL> /

INDEX_NAME
--------------------------------------------------------------------------------
ORITAB_A_PK

+++4 COPY相关对象(索引,触发器,视图等)

SQL> INSERT INTO ORI_TAB VALUES(51,'NW');

1 row created.

SQL> COMMIT;

Commit complete.

SQL> SELECT COUNT(*) FROM ORI_TAB;

  COUNT(*)
----------
 51              <<<<源本是51条记录

SQL> SELECT COUNT(*) FROM INT_TAB;

  COUNT(*)
----------
 50              <<<<新表依旧是50条记录

SQL> ed
Wrote file afiedt.buf

  1  DECLARE
  2    num_errors PLS_INTEGER;
  3  BEGIN
  4    DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS('T1',
  5            'ORI_TAB',
  6            'INT_TAB',
  7            DBMS_REDEFINITION.CONS_ORIG_PARAMS,
  8            TRUE,
  9            TRUE,
 10            TRUE,
 11            TRUE,
 12            num_errors);
 13* END;
SQL> /

PL/SQL procedure successfully completed.

SQL> SELECT COUNT(*) FROM ORI_TAB;

  COUNT(*)
----------
 51

SQL> SELECT COUNT(*) FROM INT_TAB;

  COUNT(*)
----------
 50

SQL> select index_name from user_indexes where table_name='INT_TAB';

INDEX_NAME
--------------------------------------------------------------------------------
TMP$$_ORITAB_A_PK0                   <<<<索引已经被自动创建


+++5 源表与新表做同步
SQL> exec dbms_redefinition.sync_interim_table('T1','ORI_TAB','INT_TAB');

PL/SQL procedure successfully completed.

SQL> SELECT COUNT(*) FROM ORI_TAB;

  COUNT(*)
----------
 51

SQL> SELECT COUNT(*) FROM INT_TAB;

  COUNT(*)
----------
 51                 <<<<<说明如果源表被更新频繁,我们也可以通过该方法使源表与新表尽可能多地进行数据同步
 
 
注:SYNC的程序决定了下面一步FINSH的时间长短,因为在线重定义方法在最后一步会对表持有表级排他锁。

+++6 完成在线重定义
SQL> exec dbms_redefinition.finish_redef_table('T1','ORI_TAB','INT_TAB');

PL/SQL procedure successfully completed.

另一个窗口查询源表ORI_TAB:
SQL> /

  COUNT(*)
----------
 51

SQL> /
/
/                 <<<<<<<此时持有表级排他锁

  COUNT(*)
----------
 51

SQL>
  COUNT(*)
----------
 51

SQL>
  COUNT(*)
----------
 51

 

至此,表的在线重定义就算完成了,原理就是通过一个临时段对旧数据进行同步,最终将旧数据段切换为生产。因此该方法对可用空间有要求,需要额外的空间进行数据同步并保存。

 

-------------------------------------------------------------------------------------------------

本文来自于我的技术博客 http://blog.csdn.net/robo23

转载请标注源文链接,否则追究法律责任!

<think>嗯,用户的问题是关于Oracle数据库中的DBMS_REDEFINITION包的CAN_REDEF_TABLE过程的用法和示例。他们想检查某张是否支持在线重定义。首先,我需要回忆一下这个包的作用和具体步骤。 我记得DBMS_REDEFINITION包用于在线重定义结构,比如将普通转为分区,而不影响业务运行。而CAN_REDEF_TABLE过程是用来验证是否满足在线重定义的条件。用户需要知道如何正确调用这个过程,以及具体的示例。 根据用户提供的引用内容,特别是引用[1]、[3]、[4]和[5],可以看到示例中的调用方式是使用exec命令执行存储过程,参数包括模式名、名和选项。比如引用[1]中的例子是exec DBMS_REDEFINITION.CAN_REDEF_TABLE('TEST','CNDBA',DBMS_REDEFINITION.CONS_USE_PK)。这里第三个参数是选项,可能影响检查的条件。 接下来,我需要确认参数的正确顺序和可选值。根据Oracle文档,CAN_REDEFINITION的第三个参数通常指定约束类型,比如使用主键(CONS_USE_PK)或ROWID(CONS_USE_ROWID)。这可能影响到在线重定义的方式,用户需要根据的结构选择合适的选项。 另外,用户需要知道如何执行这个存储过程,是否需要在SQL*Plus或其他工具中使用。示例中的exec命令适用于SQL*Plus环境。如果用户使用的是其他客户端工具,可能需要调整语法,比如使用BEGIN...END块。 可能的问题点包括参数顺序是否正确,是否需要指定所有参数,以及如何处理可能的错误。例如,如果没有主键,是否必须使用ROWID选项?这时可能需要引用文档中的说明,如引用[4]提到的Oracle 12c文档链接,解释不同选项的适用情况。 用户提供的引用[5]提到在12c中finish_redef_table可以指定锁定时间,这可能与CAN_REDEF_TABLE无关,但需要注意版本差异对过程的影响。需要确认CAN_REDEF_TABLE在不同版本中的行为是否一致。 总结回答的结构应该是:先说明CAN_REDEF_TABLE的作用,然后给出使用步骤,包括参数说明和示例代码,最后提供注意事项和相关引用。确保示例覆盖有主键和无主键的情况,并指出常见错误和解决方法。 还需要生成相关问题,可能涉及在线重定义的后续步骤、参数选项的详细解释、错误处理等,确保用户能够全面了解整个流程。</think>### 逐步解答 使用Oracle数据库的$DBMS\_REDEFINITION.CAN\_REDEF\_TABLE$过程可以验证是否支持在线重定义。以下是具体方法及示例: --- #### **步骤说明** 1. **验证条件** 该过程检查是否满足在线重定义要求: - 必须存在主键或允许使用$ROWID$作为唯一标识 - 不能是临时、物化视图或包含某些特殊列类型[^4] 2. **参数说明** ```sql DBMS_REDEFINITION.CAN_REDEF_TABLE( uname IN VARCHAR2, -- 模式名 tname IN VARCHAR2, -- 名 options_flag IN BINARY_INTEGER := CONS_USE_PK -- 约束类型选项 ); ``` - $options\_flag$可选值: - $CONS\_USE\_PK$(默认):使用主键作为唯一标识 - $CONS\_USE\_ROWID$:当无主键时使用$ROWID$[^3] 3. **执行验证** - **有主键的** ```sql SQL> EXEC DBMS_REDEFINITION.CAN_REDEF_TABLE('SCHEMA_NAME', 'TABLE_NAME'); ``` - **无主键的** ```sql SQL> EXEC DBMS_REDEFINITION.CAN_REDEF_TABLE('SCHEMA_NAME', 'TABLE_NAME', DBMS_REDEFINITION.CONS_USE_ROWID); ``` 4. **错误处理** - 若验证失败会抛出$ORA-12090$错误,需检查结构或参数设置[^1] - 示例错误: ``` ORA-12090: 无法在线重新定义 "SCHEMA.TABLE" ``` --- #### **完整示例** ```sql -- 检查test用户下的cndba是否支持在线重定义(使用主键) SQL> EXEC DBMS_REDEFINITION.CAN_REDEF_TABLE('TEST', 'CNDBA', DBMS_REDEFINITION.CONS_USE_PK); ``` 若返回无错误提示,则支持在线重定义。 --- #### **注意事项** 1. 确保操作期间无DDL变更 2. Oracle 12c及以上版本支持指定$FINISH\_REDEF\_TABLE$的锁定时间[^5] 3. 重定义前需创建中间并同步数据[^2] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值