6

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 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? If it UPDATEs first, and then INSERTs, the index/constraint will not be invalidated during execution. But if it first INSERTs, and then UPDATEs, the index will be temporary invalidated and the statement might fail?.

Can someone explain in detail (or point in the right direction) how Oracle RDBMS handles such issues? Furthermore, is the handling the same when using the LOG ERRORS INTO clause?

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. 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). When I look into the ERROR table, I see ORA-00001: unique constraint (XXX.YYY) violated on an INSERT operation. I can insert this record from the ERROR table into main table without causing unique constraint failure. 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. Seems like the error logging mechanism doesn't behave in an atomic way.

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:

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; I always get: 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...

EDIT4: The best answer I have marked as accepted, although the question itself is not answered completely. It seems it is just a bug in Oracle.

Davor Josipovic
  • 5,296
  • 1
  • 39
  • 57
  • 1
    What *exact* database version are your running? Metalink contains a couple of docs about bugs regarding MERGE and ORA-00001. – Frank Schmitt Mar 18 '15 at 10:56
  • 1
    Alos, have you tried running your MERGE without error logging? Metalink 17449815 ("Index inconsistency is observed for a MERGE sql with both an INSERT and UPDATE branch and ERROR LOGGING (LOG ERRORS INTO clause) are also specified") might be relevant. – Frank Schmitt Mar 18 '15 at 10:59
  • @FrankSchmitt Good idea to see it as a bug. Version is 11.2.0.4.0. I'll look into it. I did try running it without the ERROR LOGGING clause, but never hit any constraints. It is a production environment, so I can not remove the clause indefinitely. – Davor Josipovic Mar 18 '15 at 12:26
  • The first ACID property of a transaction is Atomicity. This means that either a transaction succeeds and all actions done are commited, either it fails at one point and all that has been done is rolled back which means it is like nothing have been done. – Lalit Kumar B Mar 18 '15 at 16:08

2 Answers2

5

This merge never fails.

This is explained with examples here: Database Concepts - 5. Data Integrity

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. 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.



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.

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. The same mechanism is used for all types of constraints, not just self-referential constraints.

krokodilko
  • 35,300
  • 7
  • 55
  • 79
  • Thanks for the lead. Note that constraints are only part of the question. Unique index processing is something quite different. I'll come back after I go through that documentation. – Davor Josipovic Dec 10 '14 at 19:23
  • Concerning constraints, you are absolutely right: they are checked at the end of the statement and should not cause problems. But I find it difficult to understand how the same applies to indexes. Indexes are maintained during execution and for unique indexes the rowid is not considered "part of the key". I just went through the _Concepts Manual_ and _Kyte’s DB Architecture_, but couldn’t find anything useful about index handling within a statement. – Davor Josipovic Dec 10 '14 at 22:14
  • But why do you care about indexes maintanance during an update ? They clearly say: your SQL statement never fails unless **constraints are violated at the end of the transaction execution.**. A database transaction must be ACID: http://en.wikipedia.org/wiki/ACID, esspecially must comply with consistency rules - if for example there is a constraint `A+B=100`, then the rule must be independent of the order of internal operations within the transaction. Whether the order is 'A+B=100' or 'B+A=100', it must succeed in both cases, it can't fail in the second case 'B+A=100' because the order is wrong – krokodilko Dec 11 '14 at 00:48
  • Yes, I agree. But remove the unique constraint in the above example (and leave only the unique index), and the statement will fail on the unique index, not on the constraint. If an unique index is equivalent to nondeferrable unique constraint, then OK, but that doesn’t seem to always be the case: foreign keys can only be created on unique constraints, not indexes. Also, an unique function based index has no unique constraint equivalent. So I don’t find it easy to believe that constraint checking also holds for index checking. – Davor Josipovic Dec 11 '14 at 09:32
  • And that’s not all. I have `MERGE` statements running for several tens of hours with `LOG ERRORS INTO` clause. 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). That’s the reason behind my initial question and unique index maintenance. – Davor Josipovic Dec 11 '14 at 10:06
4

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. 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. So I wonder why the error is logged in the first place.

Be sure you have the entire information in one Merge statement. 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.

  • session 1: merge using select 4 as id, 'b' as name from dual (the error is inserted in log)
  • session 2: merge using select 2 as id, null as name from dual commit ok
  • session 3: you retry the insert and it works

)

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.

Florin Ghita
  • 17,525
  • 6
  • 57
  • 76
  • Afaik, no other statement is manipulating the table. From the ERR-table's ora_err_tag$ message I can conclude that the above MERGE statement is causing the failure. You have some valid points though. I'll look into them. I also updated my question (edit3). – Davor Josipovic Mar 20 '15 at 16:54