MERGE Statement & constraint violations
Oracle database ALWAYS provides Consistent-Read. This is a very powerful feature in Oracle and there are hundereds of articles on-line about this.
Simple description of Consistent Read is
When a (reading) query starts, it is important that the values of the rows selected are the same as when the query started, even if another session has changed those rows. This is refered to as read consistency. Read consistency is achieved through the SCN (system change number). (REF: http://www.adp-gmbh.ch/ora/concepts/consistent_read.html)
A somewhat bit more technical details can be found at http://dioncho.wordpress.com/2009/04/18/simple-and-stupid-test-on-consistent-read/
However, what’s Consistent Read got to do with MERGE statement that this article is about?
MERGE statement allows a developer to take a source result set (result of a query) and MERGE that with a target result set. Syntax is documented at http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm.
Key points from the doumantation (above) are
- Use the
MERGEstatement to select rows from one or more sources for update or insertion into a table or view. You can specify conditions to determine whether to update or insert into the target table or view.
- This statement is a convenient way to combine multiple operations. It lets you avoid multiple
MERGEis a deterministic statement. That is, you cannot update the same row of the target table multiple times in the same
One of our developer developed a very complex MERGE statement. The program worked fine for most of the time, however at seeming random instances (for spcific input) the statement failed with Unique Key constraint Violation
It took a deeper understanding of consistent read to not only resolve the problem, but also leverage MERGE statement better in future. I hope our experience help others who are learning about Oracle.
I’ll explain this experience with simplified example.
select * from v$version where rownum < 2; Oracle Database 11g Enterprise Edition Release 126.96.36.199.0 - Production
We’ll set up two small tables src (source) and tgt (target with PK).
drop table tgt; drop table src; create table tgt as select 1 c, 'A' str from dual ; alter table tgt add primary key (c); create table src as select 1 c, 'B' str from dual union select 2 c, 'B' str from dual union select 2 c, 'C' str from dual ;
If the following MERGE state is executed, what will be the result?
merge into tgt using src on (tgt.c = src.c) when matched then update set tgt.str = src.str when not matched then insert (tgt.c,tgt.str) values (src.c, src.str) ;
Developer was very flustered with this error and called it a bug in Oracle. I am not sure if other databases behave this way.
The expectation was based “row-by-row” thought process.
It was expected that when a matching row is encountered the row will be updated, but when “first non-matching row” is encountered it will be treated as an INSERT and subsequent rows with same key will be treated as UPDATE.
This was misplaced expectation.
Oracle ALWAY performs in read-consistent manner.For Merge statement both the source and target result sets are determined at the time when execution of the statement begins.
Therefore, in the example above both rows from source (src) with key value of 2 are treated as INSERT. Not the first one as INSERT and second one as UPDATE.
With this understanding, resolution of the problem is simple, but with misplaced expectation the struggle continues.
Hope this will save others from some undue struggle.