每行或每个语句的MERGE语句唯一索引/约束验证?

Dav*_*vic 6 sql oracle oracle11g merge-statement

假设我有以下具有以下约束的表:

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   
Run Code Online (Sandbox Code Playgroud)

现在假设我执行以下操作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   
Run Code Online (Sandbox Code Playgroud)

以上会MERGE 不会失败?如果是UPDATE第一个,然后INSERT是s,则索引/约束在执行期间不会失效.但是,如果它首先INSERT是s,然后UPDATE是s,那么索引将暂时失效并且语句可能会失败?

有人可以详细解释(或指向正确的方向)Oracle RDBMS如何处理此类问题?此外,使用该LOG ERRORS INTO条款时处理是否相同?

我提出这个问题以及为什么需要解决方案的主要原因是:我使用LOG ERRORS INTO子句运行了几个小时的MERGE语句.错误记录似乎作为自治事务工作.一些唯一的约束错误(基于唯一索引)在语句完成upserting之前记录(其中,我看到序列正在上升),我不知道为什么(尽管最后,在upserting之后,没有唯一的约束应该是无效).当我查看ERROR表时,我看到ORA-00001:INSERT操作违反了唯一约束(XXX.YYY).我可以将此记录从ERROR表插入主表,而不会导致唯一约束失败.所以我想知道为什么首先记录错误.

编辑:下面的答案断言,当执行一个语句时,约束会在语句结束时强制执行.我理解并同意(虽然我想知道在这种情况下有关索引维护的更多细节).我不明白以及为什么这个问题仍然没有得到解答是为什么我有这些ORA-00001:唯一约束(XXX.YYY)违反了他们不应该记录的错误.似乎错误记录机制不以原子方式运行.

EDIT2:

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
Run Code Online (Sandbox Code Playgroud)

编辑3: 我玩了一下,能够重现这个错误:

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;
Run Code Online (Sandbox Code Playgroud)

在最后select * from err_test;我总是得到:ORA-00001: unique constraint (XXX.CONSTR) violated.现在奇怪的是,真正的MERGE语句(在生产中)不再在PARALLEL中起作用了,有时我仍然会收到此错误...

编辑4: 我已经标记为已接受的最佳答案,尽管问题本身没有完全回答.它似乎只是Oracle中的一个错误.

kro*_*lko 5

这种合并永远不会失败

这里用例子解释这一点:数据库概念 - 5.数据完整性

对于不可破解的约束(默认):

在不可延迟的约束中,Oracle数据库永远不会将约束的有效性检查推迟到事务结束.相反,数据库会检查每个语句末尾的约束.如果违反约束,则语句回滚.



上述方法是,在整个单个SQL语句的末尾检查约束,但在执行期间不检查.



下面,在本文档中,您可以找到两个事务示例,它们在执行期间"内部"违反了某些约束规则,但最后它们实现了所有约束,并且存在合法性,因为:

...因为数据库在语句完成后有效地检查了约束.图5-4显示数据库在检查约束之前执行整个SQL语句的操作.

最后他们还写道:

本节中的示例说明了INSERT和UPDATE语句中的约束检查机制,但数据库对所有类型的DML语句使用相同的机制.相同的机制用于所有类型的约束,而不仅仅是自引用约束.