简体   繁体   English

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

[英]MERGE statement unique index/constraint validation per row or per statement?

Suppose I have the following table with the following constraints: 假设我有以下具有以下约束的表:

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   

Suppose now that I do the following MERGE : 假设我现在做以下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   

Will the above MERGE ever fail? 将上述MERGE 失败吗? If it UPDATE s first, and then INSERT s, the index/constraint will not be invalidated during execution. 如果首先UPDATE ,然后INSERT ,则执行期间索引/约束不会失效。 But if it first INSERT s, and then UPDATE s, the index will be temporary invalidated and the statement might fail?. 但是,如果它首先INSERT ,然后UPDATE s,索引将暂时失效,语句可能会失败?

Can someone explain in detail (or point in the right direction) how Oracle RDBMS handles such issues? 有人可以详细解释(或指向正确的方向)Oracle RDBMS如何处理此类问题? Furthermore, is the handling the same when using the LOG ERRORS INTO clause? 此外,使用LOG ERRORS INTO子句时处理是否相同?

Main reason why I ask this question and why I need a solution: I have MERGE statements running for several hours with LOG ERRORS INTO clause. 我提出这个问题以及为什么需要解决方案的主要原因是:我使用LOG ERRORS INTO子句运行了几个小时的MERGE语句。 The error logging seems to work as an autonomous transaction. 错误记录似乎作为自治事务工作。 Some unique constraint errors (based on unique indexes) are logged far before the statement finishes upserting (among others, I see the sequence going up), and I do not know why (although in the end, after upserting, no unique constraint should be invalidated). 一些唯一的约束错误(基于唯一索引)在语句完成upserting之前记录(其中,我看到序列正在上升),我不知道为什么(尽管最后,在upserting之后,没有唯一的约束应该是无效)。 When I look into the ERROR table, I see ORA-00001: unique constraint (XXX.YYY) violated on an INSERT operation. 当我查看ERROR表时,我看到ORA-00001:INSERT操作违反了唯一约束(XXX.YYY)。 I can insert this record from the ERROR table into main table without causing unique constraint failure. 我可以将此记录从ERROR表插入主表,而不会导致唯一约束失败。 So I wonder why the error is logged in the first place. 所以我想知道为什么首先记录错误。

EDIT: The answers below assert that when a statement is executed, the constraints are enforced at the end of the statement. 编辑:下面的答案断言,当执行一个语句时,约束会在语句结束时强制执行。 I understand and agree (while I would like to know more details about index maintenance in such scenarios). 我理解并同意(虽然我想知道在这种情况下有关索引维护的更多细节)。 What I do not understand and why this question is still not answered is why I am having these ORA-00001: unique constraint (XXX.YYY) violated errors logged while they should not be. 我不明白以及为什么这个问题仍然没有得到解答是为什么我有这些ORA-00001:唯一约束(XXX.YYY)违反了他们不应该记录的错误。 Seems like the error logging mechanism doesn't behave in an atomic way. 似乎错误记录机制不以原子方式运行。

EDIT2: 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

EDIT3: I played a bit and was able to reproduce this error: 编辑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;

In the last select * from err_test; 在最后一个select * from err_test; I always get: ORA-00001: unique constraint (XXX.CONSTR) violated . 我总是得到: ORA-00001: unique constraint (XXX.CONSTR) violated Now the strange thing is that the real MERGE statement (in production) doesn't work in PARALLEL any more, and I still get this error sometimes... 现在奇怪的是,真正的MERGE语句(在生产中)不再在PARALLEL中起作用了,有时我仍然会收到此错误...

EDIT4: The best answer I have marked as accepted, although the question itself is not answered completely. 编辑4:我已经标记为已接受的最佳答案,尽管问题本身没有完全回答。 It seems it is just a bug in Oracle. 它似乎只是Oracle中的一个错误。

This merge never fails. 这种合并永远不会失败

This is explained with examples here: Database Concepts - 5. Data Integrity 这里用例子解释这一点: 数据库概念 - 5.数据完整性

For a not defferrable constrains (default): 对于不可破解的约束(默认):

In a nondeferrable constraint, Oracle Database never defers the validity check of the constraint to the end of the transaction. 在不可延迟的约束中,Oracle数据库永远不会将约束的有效性检查推迟到事务结束。 Instead, the database checks the constraint at the end of each statement. 相反, 数据库会检查每个语句末尾的约束。 If the constraint is violated, then the statement rolls back. 如果违反约束,则语句回滚。



The above means, that constraints are checked at the end of the entire single SQL statement , but not during their execution. 上述方法是,在整个单个SQL语句的末尾检查约束,但在执行期间不检查。



Below, in this documentation, you can find two examples of transactions, that "internally", during their execution, violate some constraint rules, but at the end they fulfill all constraint, and there are legal, because: 下面,在本文档中,您可以找到两个事务示例,它们在执行期间“内部”违反了某些约束规则,但最后它们实现了所有约束,并且存在合法性,因为:

... because the database effectively checks constraints after the statement completes. ...因为数据库在语句完成后有效地检查了约束。 Figure 5-4 shows that the database performs the actions of the entire SQL statement before checking constraints. 图5-4显示数据库在检查约束之前执行整个SQL语句的操作。

In the end they also wrote that: 最后他们还写道:

The examples in this section illustrate the constraint checking mechanism during INSERT and UPDATE statements, but the database uses the same mechanism for all types of DML statements. 本节中的示例说明了INSERT和UPDATE语句中的约束检查机制, 但数据库对所有类型的DML语句使用相同的机制。 The same mechanism is used for all types of constraints, not just self-referential constraints. 相同的机制用于所有类型的约束,而不仅仅是自引用约束。

The "LOG ERRORS INTO" part of the job, as the other users pointed, happens after the statement was executed(update and insert part), while checking constraints. 正如其他用户指出的那样,作业的“LOG ERRORS INTO”部分发生在执行语句(更新和插入部分)之后, 同时检查约束。 So you can have errors inserted before the constraint checking is finished. 因此,您可以在约束检查完成之前插入错误。 This is why you see the error inserted before the statement is totally finished. 这就是为什么你看到在语句完成之前插入错误的原因。

And as an answer for this observation: 作为这种观察的答案:

I can insert this record from the ERROR table into main table without causing unique constraint failure. 我可以将此记录从ERROR表插入主表,而不会导致唯一约束失败。 So I wonder why the error is logged in the first place. 所以我想知道为什么首先记录错误。

Be sure you have the entire information in one Merge statement. 确保在一个Merge语句中包含完整信息。 if you don't update the value in the same statement but in another which occurs between your failed insert and your retry, things are explainable. 如果您不更新同一语句中的值,但是在失败的插入和重试之间发生的另一个值中,则可以解释这些问题。

(What I mean is the records in the USING part are not in the same statement. (我的意思是USING部分的记录不在同一个声明中。

  • session 1: merge using select 4 as id, 'b' as name from dual (the error is inserted in log) 会话1:使用select 4 as id, 'b' as name from dual合并select 4 as id, 'b' as name from dual (错误插入日志中)
  • session 2: merge using select 2 as id, null as name from dual commit ok 会话2:使用select 2 as id, null as name from dual合并select 2 as id, null as name from dual提交ok的select 2 as id, null as name from dual合并
  • session 3: you retry the insert and it works 第3节:你重试插入,它的工作原理

)

If you can reproduce the error with one statement, that would be a problem. 如果您可以使用一个语句重现错误,那将是一个问题。 But you have many sessions in your environment. 但是你的环境中有很多会话。 Please check the source of your Merge statements. 请检查合并声明的来源。 You may have late arrivals, or something like this. 您可能有迟到或类似的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM