Upsert / Merge within the Same Table (Oracle)
Okay, I'm a huge fan of using Oracle's %ROWTYPE
to define records structures. I love writing code like the following:
DECLARE rec_contract CONTRACT%ROWTYPE := Null; BEGIN -- ... retrieval logic and business logic ... -- "Set" logic UPDATE CONTRACT SET ROW = rec_contract WHERE CONTRACT_ID = rec_contract.CONTRACT_ID; IF (SQL%ROWCOUNT = 0) THEN INSERT INTO CONTRACT VALUES rec_contract; END IF; END;Code like the above is really convenient for writing "setters". It can easily be extended to handle
DELETE
logic too.
Now, recently I needed to copy a bunch of data within the same table but with a different primary key. I started to write a bunch of code like the above, but I just needed some of code.
To cut to the chase, the code below is an example of how to do an "UPSERT" (like a MERGE) but within the same table [which is impossible with the MERGE
command].
Source:
drop table test; clear; create table test( id number(10), name varchar2(50) ); insert into test(id,name) values (1,'First row'); insert into test using select 2,'Row two' from dual; insert into test using select 3,'Row three - unmodified' from dual; commit; select * from test; update test set (id,name) = (select 3,'Row three - modified' from test where id = 3) where id = 3; commit; select * from test;Results:
Table created 1 row inserted 1 row inserted 1 row inserted Commit complete ID NAME ----------- -------------------------------------------------- 1 First row 2 Row two 3 Row three - unmodified 1 row updated Commit complete ID NAME ----------- -------------------------------------------------- 1 First row 2 Row two 3 Row three - modified
8 comments:
a merge might be possible on the same table if you use dual. e.g. I successfully did the following recently:
merge into tcns_load_batch_ext_source target
using (select p_settlement_id EXT_SETTLEMENT_REF_NO, 'LOKI' EXT_SETTLEMENT_SYSTEM from dual) source
on ( target.EXT_SETTLEMENT_REF_NO = SOURCE.EXT_SETTLEMENT_REF_NO
and target.EXT_SETTLEMENT_SYSTEM = SOURCE.EXT_SETTLEMENT_SYSTEM)
when matched then
update
set load_status = 'ER',
load_status_msg = 'Error!: '||to_char(sysdate,'DD-MON-YYYY HH24:MI:SS')||' - SP_COPY_STRIP error! ''Pending'' TCNS_LOAD_BATCH_EXT_SOURCE record not found!'
when not matched then
insert (CNS_LOAD_BATCH_EXT_SOURCE_ID, CNS_LOAD_BATCH_ID, EXT_SETTLEMENT_REF_NO, EXT_SETTLEMENT_SYSTEM, LOAD_STATUS, LOAD_STATUS_MSG)
values (sn_tcns_load_batch_ext_source.nextval,p_cns_load_batch_id,p_settlement_id,'LOKI','ER','SP_COPY_STRIP error! ''Pending'' TCNS_LOAD_BATCH_EXT_SOURCE record not found!');
This was a way - in a single statement - for me to upsert based on some parameters coming down the line...
Paul, which version of Oracle are using?
> select p_settlement_id EXT_SETTLEMENT_REF_NO, 'LOKI' EXT_SETTLEMENT_SYSTEM from dual
To me it does not make sense to select p_settlement_id from dual.
Did your statement work?
Anonymous, that's Paul's code so I can't really comment on whether or not it worked.
p_settlement_id is most likely a parameter, so yes, it works.
Pretty nice Paul it works really well..
Anonymous, how did you make is work?
I still don't get why select p_settlement_id from dual works.
thanks!
http://stackoverflow.com/questions/237327/oracle-how-to-upsert-update-or-insert-into-a-table
MERGE INTO Employee USING dual ON ( "id"=2097153 ) WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john" WHEN NOT MATCHED THEN INSERT ("id","last","name") VALUES ( 2097153,"smith", "john" )
Post a Comment