假设我有以下具有以下约束的表:
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
先是INSERT
,然后是INSERT
s,则索引/约束在执行期间不会失效。但是如果它首先UPDATE
,然后是LOG ERRORS INTO
s,那么索引将暂时失效并且语句可能会失败?。
有人可以详细解释(或指向正确的方向)Oracle RDBMS如何处理此类问题?此外,使用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
子句时处理方法是否相同?
我提出这个问题以及为什么需要解决方案的主要原因是:我使用LOG ERRORS INTO子句运行了几个小时的MERGE语句。错误记录似乎作为自治事务工作。一些唯一的约束错误(基于唯一索引)在语句完成upserting之前记录(其中,我看到序列正在上升),我不知道为什么(尽管最后,在upserting之后,没有唯一的约束应该是无效)。当我查看ERROR表时,我看到ORA-00001:INSERT操作违反了唯一约束(XXX.YYY)。我可以将此记录从ERROR表插入主表,而不会导致唯一约束失败。所以我想知道为什么首先会记录错误。
编辑:以下答案断言,执行语句时,会在语句结束时强制执行约束。我理解并同意(虽然我想知道在这种情况下有关索引维护的更多细节)。我不明白以及为什么这个问题仍然没有得到解答是为什么我有这些ORA-00001:唯一约束(XXX.YYY)违反了他们不应该记录的错误。似乎错误记录机制不以原子方式运行。
EDIT2:
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;
EDIT3: 我玩了一下,能够重现这个错误:
select * from err_test;
在上一个ORA-00001: unique constraint (XXX.CONSTR) violated
我总是得到:{{1}}。现在奇怪的是,真正的MERGE语句(在生产中)不再在PARALLEL中起作用了,有时我仍然会收到此错误...
EDIT4: 我已经标记为接受的最佳答案,尽管问题本身并未完全回答。它似乎只是甲骨文中的一个错误。
答案 0 :(得分:5)
此合并永远不会失败。
这里用例子解释了这一点:Database Concepts - 5. Data Integrity
对于不可破解的约束(默认):
在不可延迟的约束中,Oracle数据库永远不会推迟 约束的有效性检查到交易结束。 而是,数据库检查每个结束时的约束 声明。如果违反约束,则语句滚动 回来。
上述方法是,在整个单个SQL语句的末尾检查约束,但在执行期间不检查。
下面,在本文档中,您可以找到两个事务示例,即内部&#34;在执行期间违反某些约束规则,但最后它们满足所有约束,并且有合法性,因为:< / p>
...因为数据库有效地检查后的约束 声明完成。图5-4显示了数据库执行的操作 检查约束之前整个SQL语句的操作。
最后他们还写道:
本节中的示例说明了约束检查 INSERT和UPDATE语句中的机制,但数据库使用 所有类型的DML语句都使用相同的机制。同样的机制 用于所有类型的约束,而不仅仅是自引用 约束强>
答案 1 :(得分:4)
&#34; LOG ERRORS INTO&#34;正如其他用户指出的那样,工作的一部分发生在执行语句(更新和插入部分)之后,同时检查约束。因此,您可以在约束检查完成之前插入错误。 这就是您在语句完全结束之前看到插入错误的原因。
作为这种观察的答案:
我可以将此记录从ERROR表插入到主表中而不用 导致唯一约束失败。所以我想知道为什么会记录错误 首先。
确保您在一个合并声明中包含所有信息。如果你没有在同一个语句中更新值,但是在失败的插入和重试之间发生的另一个值中,事情是可以解释的。
(我的意思是USING部分的记录不在同一声明中。
select 4 as id, 'b' as name from dual
进行合并
(错误插入日志中)select 2 as id, null as name from dual
进行合并
提交确定)
如果您可以使用一个语句重现错误,那将是一个问题。但是你的环境中有很多会话。请检查合并声明的来源。你可能会迟到,或类似的东西。