假设我有下表具有以下约束:
create table test as (
select 1 as id, 'a' as name from dual
union all
select 2, 'b' from dual
union all
select 3, 'c' from dual
);
create unique index ind on test(name);
alter table test add constraint constr unique (name);
select * from test;
ID NAME
---------- ----
1 a
2 b
3 c
现在假设我执行以下操作MERGE
:
merge into test t using (
select 4 as id, 'b' as name from dual
union all
select 2 as id, null as name from dual
) s on (s.id = t.id)
when matched then update set t.name = s.name
when not matched then insert(t.id, t.name) values(s.id, s.name)
select * from test;
ID NAME
---------- ----
1 a
2
3 c
4 b
以上MERGE
会失败吗?如果UPDATE
先s,后INSERT
s,则索引/约束在执行过程中不会失效。但是如果先INSERT
s,再UPDATE
s,索引会暂时失效,语句可能会失败?
有人可以详细解释(或指出正确的方向)Oracle RDBMS 如何处理此类问题吗?此外,使用该LOG ERRORS INTO
子句时的处理方式是否相同?
我问这个问题以及为什么我需要一个解决方案的主要原因:我的 MERGE 语句运行了几个小时,并带有 LOG ERRORS INTO 子句。错误日志似乎作为一个自主事务工作。在语句完成更新插入之前很久就记录了一些唯一约束错误(基于唯一索引)(其中,我看到序列上升),我不知道为什么(尽管最后,在更新插入之后,应该没有唯一约束无效)。当我查看 ERROR 表时,我看到 ORA-00001:INSERT 操作违反了唯一约束 (XXX.YYY)。我可以将此记录从 ERROR 表插入到主表中,而不会导致唯一约束失败。所以我想知道为什么首先记录错误。
编辑:下面的答案断言,当执行语句时,约束在语句末尾强制执行。我理解并同意(同时我想了解更多关于此类情况下的索引维护的详细信息)。我不明白为什么这个问题仍然没有回答是为什么我有这些 ORA-00001: unique constraint (XXX.YYY) 违反了错误记录,而它们不应该记录。似乎错误记录机制不是以原子方式运行的。
编辑2:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE 11.2.0.4.0 Production
TNS for Solaris: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production
EDIT3: 我玩了一下,能够重现这个错误:
drop table test;
drop table err_test;
create table test as (
select 1 as id, 'a' as name from dual
union all
select 2, 'b' from dual
union all
select 3, 'c' from dual
);
create unique index ind on test(name);
alter table test add constraint constr unique (name);
--select test.rowid, test.* from test;
BEGIN
DBMS_ERRLOG.CREATE_ERROR_LOG (
dml_table_name => 'TEST',
err_log_table_name => 'ERR_TEST');
END;
/
--truncate table err_test;
select * from err_test;
merge /*+ PARALLEL(t 2) */ into test t using (
select 4 as id, 'b' as name from dual
union all
select 2 as id, null as name from dual
) s on (s.id = t.id)
when matched then update set t.name = s.name
when not matched then insert(t.id, t.name) values(s.id, s.name)
LOG ERRORS INTO ERR_TEST('TEST,ID:'||s.id) REJECT LIMIT UNLIMITED;
select * from err_test;
在最后select * from err_test;
我总是得到:ORA-00001: unique constraint (XXX.CONSTR) violated
。现在奇怪的是,真正的 MERGE 语句(在生产中)不再在 PARALLEL 中工作,有时我仍然会收到这个错误......
EDIT4: 我标记为已接受的最佳答案,尽管问题本身并没有完全回答。看来这只是Oracle中的一个错误。