一般情况下,分区表的分区字段的值是不应该修改的,如果这个值发生变化,则有可能使这条记录不再符号当前分区的分区条件,会使这条记录转移到其他分区中,物理位置(ROWID)会相应变化。
Oracle提供了ENABLE ROW MOVEMENT语句,增加了分区的功能,允许修改分区列,允许记录转移到其他分区,且Oracle会自动维护索引。
虽然,Oracle提供的这个功能很方便,但是也会带来相应的副作用——ROWID不可避免的要发生变化。其中最直接的影响就是LOGMINER,这种发生了ENABLE ROW MOVEMENT的操作,将无法使用LOGMINER中的SQL_UNDO来进行恢复。
具体情况来看下面的例子:
SQL> CREATE TABLE TEST_PART (ID NUMBER) PARTITION BY RANGE (ID)
2 (PARTITION P1 VALUES LESS THAN (100), PARTITION P2 VALUES LESS THAN (MAXVALUE))
3 ENABLE ROW MOVEMENT;
表已创建。
SQL> INSERT INTO TEST_PART VALUES (20);
已创建 1 行。
SQL> INSERT INTO TEST_PART VALUES (20);
已创建 1 行。
SQL> COMMIT;
提交完成。
SQL> COL MEMBER FORMAT A70
SQL> SELECT * FROM V$LOGFILE;
GROUP# STATUS TYPE MEMBER
---------- ------- ------- ---------------------------------------
3 STALE ONLINE F:ORACLEORADATATEST4REDO03.LOG
2 STALE ONLINE F:ORACLEORADATATEST4REDO02.LOG
1 ONLINE F:ORACLEORADATATEST4REDO01.LOG
SQL> ALTER SYSTEM SWITCH LOGFILE;
系统已更改。
SQL> SELECT * FROM V$LOGFILE;
GROUP# STATUS TYPE MEMBER
---------- ------- ------- ----------------------------------------
3 STALE ONLINE F:ORACLEORADATATEST4REDO03.LOG
2 ONLINE F:ORACLEORADATATEST4REDO02.LOG
1 ONLINE F:ORACLEORADATATEST4REDO01.LOG
SQL> UPDATE TEST_PART SET ID = 120 WHERE ROWNUM = 1;
已更新 1 行。
SQL> COMMIT;
提交完成。
SQL> ALTER SYSTEM SWITCH LOGFILE;
系统已更改。
SQL> SELECT * FROM V$LOGFILE;
GROUP# STATUS TYPE MEMBER
---------- ------- ------- --------------------------------------
3 ONLINE F:ORACLEORADATATEST4REDO03.LOG
2 ONLINE F:ORACLEORADATATEST4REDO02.LOG
1 ONLINE F:ORACLEORADATATEST4REDO01.LOG
SQL> EXEC SYS.DBMS_LOGMNR.ADD_LOGFILE(LOGFILENAME => 'F:ORACLEORADATATEST4REDO02.LOG', -
> OPTIONS => SYS.DBMS_LOGMNR.NEW)
PL/SQL 过程已成功完成。
SQL> EXEC SYS.DBMS_LOGMNR.START_LOGMNR(OPTIONS => SYS.DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG)
PL/SQL 过程已成功完成。
SQL> SELECT SQL_UNDO, SQL_REDO FROM V$LOGMNR_CONTENTS;
SQL_UNDO
------------------------------------------------------------------
SQL_REDO
------------------------------------------------------------------
set transaction read write;
update "YANGTK"."TEST_PART" set "ID" = '20' where "ID" = '120' and ROWID = 'AAAB5OAAFAAAAegAAA';
update "YANGTK"."TEST_PART" set "ID" = '120' where "ID" = '20' and ROWID = 'AAAB5OAAFAAAAegAAA';
insert into "YANGTK"."TEST_PART"("ID") values ('120');
delete from "YANGTK"."TEST_PART" where "ID" = '120' and ROWID = 'AAAB5OAAFAAAAegAAA';
delete from "YANGTK"."TEST_PART" where "ID" = '120' and ROWID = 'AAAB5PAAFAAAAeoAAA';
insert into "YANGTK"."TEST_PART"("ID") values ('120');
commit;
SQL> exec sys.dbms_logmnr.end_logmnr
PL/SQL 过程已成功完成。
SQL> delete from "YANGTK"."TEST_PART" where "ID" = '120' and ROWID = 'AAAB5PAAFAAAAeoAAA';
已删除 1 行。
SQL> insert into "YANGTK"."TEST_PART"("ID") values ('120');
已创建 1 行。
SQL> update "YANGTK"."TEST_PART" set "ID" = '20' where "ID" = '120' and ROWID = 'AAAB5OAAFAAAAegAAA';
已更新0行。
可以看到,一个UPDATE语句被ORACLE拆成了三条语句。首先是对当前的记录进行更新(指定ROWID),第二步删除指定ROWID的记录,第三步用更新后的值重新插入记录。执行完第三步,新的记录已经插入到分区P2中了。
如果按照相反的顺序执行SQL_UNDO,却发现无法恢复到以前的状态。首先删除分区P2中的记录,这没有问题。第二步,插入新的记录,这里已经有问题了,插入时无法指定ROWID,记录仍然会插入到P2分区中。最后进行更新,这是指定的ROWID是P1分区中的ROWID,这个ROWID在当前根本就不存在,因此,已经无法恢复到原来的状态了。