Oracle:如何UPSERT(更新或插入表)?

UPSERT操作可以更新或在表中插入一行,具体取决于表中是否有一行与数据匹配的行:

if table t has a row exists that has key X: update t set mystuff... where mykey=X else insert into t mystuff... 

由于Oracle没有特定的UPSERT语句,最好的办法是什么?

MERGE语句在两个表之间合并数据。 使用DUAL允许我们使用这个命令。

 create or replace procedure ups(xa number) as begin merge into mergetest m using dual on (a = xa) when not matched then insert (a,b) values (xa,1) when matched then update set b = b+1; end ups; / drop table mergetest; create table mergetest(a number, b number); call ups(10); call ups(10); call ups(20); select * from mergetest; AB ---------------------- ---------------------- 10 2 20 1 

上面的PL / SQL的双重例子是伟大的,因为我想要做类似的事情,但我想它的客户端…所以这里是我用来发送类似的声明直接从一些C#

 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" ) 

然而,从C#的角度来看,这比提供更新要慢一些,看看受影响的行是否为0,如果是的话进行插入。

没有例外检查的另一种选择:

 UPDATE tablename SET val1 = in_val1, val2 = in_val2 WHERE val3 = in_val3; IF ( sql%rowcount = 0 ) THEN INSERT INTO tablename VALUES (in_val1, in_val2, in_val3); END IF; 

MERGE的另一种选择(“老式的方式”):

 begin insert into t (mykey, mystuff) values ('X', 123); exception when dup_val_on_index then update t set mystuff = 123 where mykey = 'X'; end; 
  1. 插入如果不存在
  2. 更新:
    
 INSERT INTO mytable(id1,t1) 
  选择11,'x1'从双 
   WHERE NOT EXISTS(SELECT id1 FROM mytble WHERE id1 = 11); 

更新mytable SET t1 ='x1'WHERE id1 = 11;

正如Tim Sylvester的评论所指出的,迄今为止所给出的答案在面对同时访问时安全的,并且会在种族方面提出例外。 为了解决这个问题,insert / update组合必须被包装在某种循环语句中,以便在发生异常的情况下重试整个事件。

作为一个例子,下面是Grommit的代码如何在一个循环中打包,以便在并发运行时保证安全:

 PROCEDURE MyProc ( ... ) IS BEGIN LOOP BEGIN 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" ); EXIT; -- success? -> exit loop EXCEPTION WHEN NO_DATA_FOUND THEN -- the entry was concurrently deleted NULL; -- exception? -> no op, ie continue looping WHEN DUP_VAL_ON_INDEX THEN -- an entry was concurrently inserted NULL; -- exception? -> no op, ie continue looping END; END LOOP; END; 

NB在交易模式SERIALIZABLE ,我不推荐顺便说一句,你可能会遇到ORA-08177:不能序列化访问这个事务异常,而不是。

我想Grommit的回答,除了它需要dupe值。 我发现解决方案可能会出现一次: http : //forums.devshed.com/showpost.php?p=1182653&postcount=2

 MERGE INTO KBS.NUFUS_MUHTARLIK B USING ( SELECT '028-01' CILT, '25' SAYFA, '6' KUTUK, '46603404838' MERNIS_NO FROM DUAL ) E ON (B.MERNIS_NO = E.MERNIS_NO) WHEN MATCHED THEN UPDATE SET B.CILT = E.CILT, B.SAYFA = E.SAYFA, B.KUTUK = E.KUTUK WHEN NOT MATCHED THEN INSERT ( CILT, SAYFA, KUTUK, MERNIS_NO) VALUES (E.CILT, E.SAYFA, E.KUTUK, E.MERNIS_NO); 

关于两个解决方案的说明建议:

1)插入,如果异常则更新,

要么

2)更新,如果sql%rowcount = 0,然后插入

是否插入或更新的问题也取决于应用程序。 你期望更多的插入或更新? 最有可能成功的人应该先走。

如果你选错了,你会得到一堆不必要的索引读取。 没有什么大不了的,但还是需要考虑。

我一直在使用第一个代码示例多年。 没有发现通知,而不是计数。

 UPDATE tablename SET val1 = in_val1, val2 = in_val2 WHERE val3 = in_val3; IF ( sql%notfound ) THEN INSERT INTO tablename VALUES (in_val1, in_val2, in_val3); END IF; 

下面的代码是可能是新的和改进的代码

 MERGE INTO tablename USING dual ON ( val3 = in_val3 ) WHEN MATCHED THEN UPDATE SET val1 = in_val1, val2 = in_val2 WHEN NOT MATCHED THEN INSERT VALUES (in_val1, in_val2, in_val3) 

在第一个示例中,更新执行索引查找。 它必须,为了更新正确的行。 Oracle打开一个隐式游标,我们用它来包装一个相应的插入,所以我们知道插入只有当密钥不存在时才会发生。 但插入是一个独立的命令,它必须做第二次查找。 我不知道合并命令的内部工作原理,但由于命令是一个单元,因此Oracle可以通过单个索引查找执行正确的插入或更新。

我认为当你需要完成一些处理时,合并会更好,这意味着从某些表中获取数据并更新表,可能会插入或删除行。 但是对于单行情况,您可能会考虑第一种情况,因为语法更常见。

复制并粘贴示例将一个表插入另一个表,使用MERGE:

 CREATE GLOBAL TEMPORARY TABLE t1 (id VARCHAR2(5) , value VARCHAR2(5), value2 VARCHAR2(5) ) ON COMMIT DELETE ROWS; CREATE GLOBAL TEMPORARY TABLE t2 (id VARCHAR2(5) , value VARCHAR2(5), value2 VARCHAR2(5)) ON COMMIT DELETE ROWS; ALTER TABLE t2 ADD CONSTRAINT PK_LKP_MIGRATION_INFO PRIMARY KEY (id); insert into t1 values ('a','1','1'); insert into t1 values ('b','4','5'); insert into t2 values ('b','2','2'); insert into t2 values ('c','3','3'); merge into t2 using t1 on (t1.id = t2.id) when matched then update set t2.value = t1.value, t2.value2 = t1.value2 when not matched then insert (t2.id, t2.value, t2.value2) values(t1.id, t1.value, t1.value2); select * from t2 

结果:

  1. b 4 5
  2. c 3 3
  3. 1 1

尝试这个,

 insert into b_building_property ( select 'AREA_IN_COMMON_USE_DOUBLE','Area in Common Use','DOUBLE', null, 9000, 9 from dual ) minus ( select * from b_building_property where id = 9 ) ; 

http://www.praetoriate.com/oracle_tips_upserts.htm

“在Oracle9i中,UPSERT可以用一个语句完成这个任务:”

 INSERT FIRST WHEN credit_limit >=100000 THEN INTO rich_customers VALUES(cust_id,cust_credit_limit) INTO customers ELSE INTO customers SELECT * FROM new_customers;