ORA-12012: error on auto execute of job 133829
ORA-12008: error in materialized view refresh path
ORA-00001: unique constraint (CODA_DW.PK_AFSPLANSUMMARY_EMPNUM) violated
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2254
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2460
ORA-06512: at "SYS.DBMS_IREFRESH", line 683
ORA-06512: at "SYS.DBMS_REFRESH", line 195
ORA-06512: at line 1
ORA-00001: unique constraint (NDMAIN.I_GPO_USR_USER_IDXU_CODE) violated
检查两边约束,发现完全一致,经过10046跟踪,发现根本原因。
下面举例重现错误:
SQL> create table test(id int,code varchar2(10));
Table created
SQL> alter table test add constraint pk_test primary key(id);
Table altered
SQL> alter table test add constraint un_test_code unique(code);
Table altered
SQL> create materialized view log on test;
Materialized view log created
SQL> insert into test values(1,'code1');
1 row inserted
SQL> insert into test values(2,'code2');
1 row inserted SQL> commit;
Commit complete
--创建快速刷新的物化视图
SQL> create materialized view mv_test refresh fast as select * from test;
Materialized view created
SQL> alter table mv_test add constraint un_mv_test_code unique(code);
Table altered
SQL> create materialized view log on mv_test;
Materialized view log created
--把code1和code2对换
SQL> update test set code='code3' where id=1;
1 row updated
SQL> update test set code='code1' where id=2;
1 row updated
SQL> update test set code='code2' where id=1;
1 row updated
SQL> commit;
Commit complete
SQL> select * from mlog$_test;
ID SNAPTIME$$ DMLTYPE$$ OLD_NEW$$ CHANGE_VECTOR$$
---------- ----------- --------- --------- --------------------
1 2008-3-31 2 U U 04
2 2008-3-31 2 U U 04
1 2008-3-31 2 U U 04
--快速刷新物化视图
SQL> exec dbms_mview.refresh('mv_test') begin dbms_mview.refresh('mv_test'); end;
ORA-12008: 实体化视图的刷新路径中存在错误
ORA-00001: 违反唯一约束条件 (SUK.UN_MV_TEST_CODE)
ORA-06512: 在 "SYS.DBMS_SNAPSHOT", line 2255
ORA-06512: 在 "SYS.DBMS_SNAPSHOT", line 2461
ORA-06512: 在 "SYS.DBMS_SNAPSHOT", line 2430
ORA-06512: 在 line 1
从后台跟踪的10046trade文件可以看到下面的信息:
--原始trace内容
=====================
PARSING IN CURSOR #18 len=64 dep=2 uid=29 ct=6 lid=29 tim=10217293845 hv=3019709084 ad='204ef138' UPDATE "SUK"."MV_TEST" SET "ID" = :1,"CODE" = :2 WHERE "ID" = :1 END OF STMT PARSE #18:c=0,e=172,p=0,cr=0,cu=0,mis=1,r=0,dep=2,og=1,tim=10217293838 BINDS
#18: kkscoacd Bind
#0 acdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00 acflg=13 fl2=0001 frm=00 csi=00 siz=24 ff=0 kxsbbbfp=0789b014 bln=22 avl=02 flg=09 value=1 --ID的值
Bind#1 acdty=01 mxl=32(10) mxlc=00 mal=00 scl=00 pre=00 acflg=13 fl2=0001 frm=01 csi=852 siz=32 ff=0 kxsbbbfp=0789b02c bln=32 avl=05 flg=09 value="code2" --code的值
Bind#2 No oacdef for this bind. --格式化后的trace内容
UPDATE "SUK"."MV_TEST" SET "ID" = :1,"CODE" = :2 WHERE "ID" = :1 call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.01 0.00 0 1 5 0 Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 0.01 0.00 0 1 5 0 --只执行一次
分析以上内容可以得到如下结论:对ID=1的数据刷新只做了一次。 其实oracle这样做是很有道理的:如果oracle完全根据MLOG$记录的日志顺序刷新数据,对于那些被多次更新的数据,不得不刷新多次,这对效率影响很大。 实际上,MLOG$只是记录对基表的那些列执行了那些操作,并不记录更新前后的值,所以无法也没必要完全重演主表的数据更新历史,所以,对于相同主键的数据执行了多次更新,只需要根据主键和主表同步一次数据即可实现与主表的数据同步。 这个机制也是导致文中开头错误的根源: mv_test刷新数据时,对于ID=1的数据,只执行一次数据刷新:update mv_test set name='code2' where id=1; 此时,mv_test中id=2的name='code2'(还没有变成code1),这必然违反code上的唯一性约束。 为了避免这个错误,可以有以下解决方法: 1、如果是唯一性约束,把这个约束设置为延迟性约束(在提交时才检验数据的合法性) 2、如果是唯一性索引,则先创建一个普通索引,然后用这个索引创建一个延迟唯一性约束。 3、如果不考虑性能或者执行计划问题,则物化视图端的约束都可以去掉,再主表保证数据的合法性即可。 这个错误只会出现在快速刷新中,完全刷新是不存在这样的问题的。