47

Possible Duplicate:
Oracle: how to UPSERT (update or insert into a table?)

Hi,

I have a table in which a record has to be modified if it already exists else a new record has to be inserted. Oracle sql doesnt accept IF EXISTS, otherwise I would have done an if - update - else - insert query. I've looked at MERGE but it only works for multiple tables. What do i do?

0

6 Answers 6

87

MERGE doesn't need "multiple tables", but it does need a query as the source. Something like this should work:

MERGE INTO mytable d
USING (SELECT 1 id, 'x' name from dual) s
ON (d.id = s.id)
WHEN MATCHED THEN UPDATE SET d.name = s.name
WHEN NOT MATCHED THEN INSERT (id, name) VALUES (s.id, s.name);

Alternatively you can do this in PL/SQL:

BEGIN
  INSERT INTO mytable (id, name) VALUES (1, 'x');
EXCEPTION
  WHEN DUP_VAL_ON_INDEX THEN
    UPDATE mytable
    SET    name = 'x'
    WHERE id = 1;
END;
Sign up to request clarification or add additional context in comments.

5 Comments

+1 I don't know about the MERGE instruction, but as for the exception DUP_VAL_ON_INDEX handling, that is definitely a nice solution, knowing that Oracle exception handling is regularly used for such behaviour! =)
+1; it should be noted that the alternative solution is generally a lot less efficient.
How to do it when ID is unknown? In example you searching row by name and want to change surname...
@Dumbo You should have a unicity constraint on the column right?
For anyone coming to this thread in the future, note that if you actually want to see the results in your OracleSQL Database after running MERGE INTO statement, make sure to commit the transaction by running COMMIT;
13
merge into MY_TABLE tgt
using (select [expressions]
         from dual ) src
   on (src.key_condition = tgt.key_condition)
when matched then 
     update tgt
        set tgt.column1 = src.column1 [,...]
when not matched then 
     insert into tgt
        ([list of columns])
     values
        (src.column1 [,...]);

Comments

11

You could use the SQL%ROWCOUNT Oracle variable:

UPDATE table1
  SET field2 = value2, 
      field3 = value3 
WHERE field1 = value1; 

IF (SQL%ROWCOUNT = 0) THEN 

  INSERT INTO table (field1, field2, field3)
  VALUES (value1, value2, value3);

END IF; 

It would be easier just to determine if your primary key (i.e. field1) has a value and then perform an insert or update accordingly. That is, if you use said values as parameters for a stored procedure.

2 Comments

If you have multiple sessions writing simultaneously, you might run into the situation that the update touches zero rows so you assume there is no row and need to do an insert, but in the mean-time someone has done an insert so your insert fails with a unique constraint violation. That's why it's important to do insert (and catch unique constraint violations) then update, not the other way around.
Worst logic i have ever seen
2

The way I always do it (assuming the data is never to be deleted, only inserted) is to

  • Firstly do an insert, if this fails with a unique constraint violation then you know the row is there,
  • Then do an update

Unfortunately many frameworks such as Hibernate treat all database errors (e.g. unique constraint violation) as unrecoverable conditions, so it isn't always easy. (In Hibernate the solution is to open a new session/transaction just to execute this one insert command.)

You can't just do a select count(*) .. where .. as even if that returns zero, and therefore you choose to do an insert, between the time you do the select and the insert someone else might have inserted the row and therefore your insert will fail.

3 Comments

Even with the insert only constraint, using two transactions could lead to uniqueness exceptions if there are multiple updaters writing to the table.
David Mann, I'm sorry I didn't understand; please clarify.
Hi Adrian, I'm just thinking about a case where the UPDATE statement is only valid if the data has not changed since the attempted INSERT, and there are multiple processes performing the inserts and updates. Unless all inserts and updates to the table are synchronized, sometimes the UPDATE may succeed and sometimes it may fail depending on how the processes interleave. It seemed like such a corner case to me until I was required to perform UPDATEs that are only valid if the state of the data since the last read is still the same.
1

HC-way :)

DECLARE
  rt_mytable mytable%ROWTYPE;
  CURSOR update_mytable_cursor(p_rt_mytable IN mytable%ROWTYPE) IS
  SELECT *
  FROM   mytable
  WHERE  ID = p_rt_mytable.ID
  FOR UPDATE;
BEGIN
  rt_mytable.ID   := 1;
  rt_mytable.NAME := 'x';
  INSERT INTO mytable VALUES (rt_mytable);
EXCEPTION WHEN DUP_VAL_ON_INDEX THEN
  <<update_mytable>>
  FOR i IN update_mytable_cursor(rt_mytable) LOOP
    UPDATE mytable SET    
      NAME = p_rt_mytable.NAME
    WHERE CURRENT OF update_mytable_cursor;
  END LOOP update_mytable;
END;

4 Comments

Nothing special about this because the basic principle of this was already posted earlier. Just for fun :)
What does "HC" mean ?
Would also add IN OUT NOCOPY to cursor parameter... But it was 3 years ago when I posted that :)
HC means Hard Core
-1

Please refer to this question if you want to use UPSERT/MERGE command in Oracle. Otherwise, just resolve your issue on the client side by doing a count(1) first and then deciding whether to insert or update.

2 Comments

You could not be less correct; Oracle has supported the MERGE statement since Oracle 9i.
The x-ref is good; it contradicts what you say, though. Not my -1, but I at least sympathize with it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.