2008-10-26 57 views
237

的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语句,因此最好的方法是什么?

回答

33

的替代合并(下称 “老办法”):

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; 
182

MERGE statement合并两个表之间的数据。使用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; 

A      B 
---------------------- ---------------------- 
10      2 
20      1 
+45

显然“融入”国家恩不是原子的。同时使用时可能会导致“ORA-0001:唯一约束”。检查是否存在匹配和插入新记录不受锁的保护,因此存在竞争条件。为了可靠地做到这一点,您需要捕获此异常,并重新运行合并或者进行简单的更新。 在Oracle 10中,可以使用“日志错误”子句在发生错误时使其继续处理其余行,并将违规行记录到另一个表,而不仅仅是停止。 – 2009-07-13 05:15:15

+1

嗨,我试图在我的查询中使用相同的查询模式,但不知何故我的查询插入重复的行。我无法找到关于DUAL表的更多信息。任何人都可以告诉我在哪里可以得到DUAL的信息,还有关于合并语法? – Shekhar 2010-10-15 06:12:05

+5

@Shekhar Dual是一个单行和一列的虚拟表格http://www.adp-gmbh.ch/ora/misc/dual.html – YogoZuno 2010-11-19 04:56:16

42

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

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; 
-5

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; 
91

上面这个在PL/SQL中的双重例子很不错,因为我想做类似的事情,但我想要它的客户端...所以这里是我用来直接发送类似语句的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,做插入,如果它是慢

不过。

-2

试试这个,

关于两个解决方案,建议
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 
) 
; 
8

的说明:

1)插入,如果异常然后更新,

2)更新,如果sql%rowcount = 0 then insert

是否插入或更新首先也是应用程序依赖的。你期待更多的插入或更多的更新?最有可能成功的人应该先走。

如果你选错了,你会得到一堆不必要的索引读数。这不是一笔巨额交易,但仍需要考虑。

13

我想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); 
21
  1. 插入,如果不存在
  2. 更新:
  
INSERT INTO mytable (id1, t1) 
    SELECT 11, 'x1' FROM DUAL 
    WHERE NOT EXISTS (SELECT id1 FROM mytble WHERE id1 = 11); 

UPDATE mytable SET t1 = 'x1' WHERE id1 = 11; 
18

无到目前为止给出的答案是安全的脸并发访问,正如蒂姆西尔维斯特的评论中指出的那样,并且会在种族的情况下引发异常。为了解决这个问题,insert/update组合必须被包装在某种循环语句中,以便在发生异常时重试整个事件。

作为一个例子,这里是如何在同时运行的金属扣眼的代码可以被包裹在一个循环,使之安全:

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, i.e. continue looping 
    WHEN DUP_VAL_ON_INDEX THEN -- an entry was concurrently inserted 
     NULL; -- exception? -> no op, i.e. continue looping 
    END; 
END LOOP; 
END; 

注:在交易模式SERIALIZABLE中,我不推荐顺便说一句,您可能会遇到 ORA-08177: can't serialize access for this transaction异常。

5

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

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可以通过单个索引查找执行正确的插入或更新。

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

0

复印&膏例如用于upserting一个表转换成另一个,与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