【问题标题】:oracle sql: update if exists else insert [duplicate]oracle sql:如果存在则更新,否则插入[重复]
【发布时间】:2011-04-30 05:38:43
【问题描述】:

可能重复:
Oracle: how to UPSERT (update or insert into a table?)

嗨,

我有一个表,如果记录已经存在,则必须在其中修改记录,否则必须插入新记录。 Oracle sql 不接受IF EXISTS,否则我会执行if - update - else - insert 查询。我看过MERGE,但它只适用于多个表。我该怎么办?

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    MERGE 不需要“多个表”,但它确实需要一个查询作为源。这样的事情应该可以工作:

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

    您也可以在 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;
    

    【讨论】:

    • +1 我不知道MERGE 指令,但至于DUP_VAL_ON_INDEX 处理异常,这绝对是一个不错的解决方案,知道Oracle 异常处理经常用于此类行为! =)
    • +1;应该注意的是,替代解决方案的效率通常要低得多。
    • ID未知怎么办?例如,您按名称搜索行并想更改姓氏...
    • @Dumbo 你应该对列有唯一性约束吧?
    • 对于以后来此线程的任何人,请注意,如果您真的想在运行 MERGE INTO 语句后在 OracleSQL 数据库中查看结果,请确保通过运行 COMMIT; 提交事务
    【解决方案2】:
    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 [,...]);
    

    【讨论】:

      【解决方案3】:

      您可以使用SQL%ROWCOUNT Oracle 变量:

      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; 
      

      确定您的主键(即field1)是否有值然后相应地执行插入或更新会更容易。也就是说,如果您将所述值用作存储过程的参数。

      【讨论】:

      • 如果您有多个会话同时写入,您可能会遇到update 触及零行的情况,因此您假设没有行并需要执行insert,但平均而言-time 有人做了insert,所以你的insert 因违反唯一约束而失败。这就是为什么重要的是先执行insert(并捕获违反唯一约束)然后执行update,而不是相反。
      • 我见过的最糟糕的逻辑
      【解决方案4】:

      我一直这样做的方式(假设数据永远不会被删除,只会插入)是

      • 首先执行insert,如果由于违反唯一约束而失败,那么您知道该行存在,
      • 然后做一个update

      不幸的是,许多框架(例如 Hibernate)将所有数据库错误(例如违反唯一约束)视为不可恢复的条件,因此并不总是那么容易。 (在 Hibernate 中,解决方案是打开一个新的会话/事务来执行这个insert 命令。)

      你不能只做一个select count(*) .. where ..,即使它返回零,因此你选择在你做selectinsert之间做一个insert,其他人可能有@ 987654328@ed 行,因此您的 insert 将失败。

      【讨论】:

      • 即使使用仅插入约束,如果有多个更新程序写入表,使用两个事务也可能导致唯一性异常。
      • 大卫曼,对不起,我没听懂;请澄清。
      • 嗨,Adrian,我只是在考虑这样一种情况,即 UPDATE 语句仅在自尝试 INSERT 后数据未更改且有多个进程执行插入和更新时才有效。除非对表的所有插入和更新都同步,否则有时 UPDATE 可能会成功,有时可能会失败,具体取决于进程的交错方式。在我被要求执行仅当自上次读取以来的数据状态仍然相同时才有效的更新,这对我来说似乎是一个极端情况。
      【解决方案5】:

      HC 方式 :)

      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;
      

      【讨论】:

      • 这没什么特别的,因为它的基本原理之前已经发布过了。只是为了好玩:)
      • “HC”是什么意思?
      • 还会将 IN OUT NOCOPY 添加到游标参数中...但那是 3 年前我发布的 :)
      • HC 表示硬核
      【解决方案6】:

      如果您想在Oracle 中使用UPSERT/MERGE 命令,请参考this 问题。否则,只需在客户端解决您的问题,首先执行count(1),然后再决定是插入还是更新。

      【讨论】:

      • 你的正确性简直不能再差了; Oracle 从 Oracle 9i 开始支持 MERGE 语句。
      • 外部参考很好;但是,它与您所说的相矛盾。不是我的-1,但我至少同情它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-04
      • 2014-10-23
      • 1970-01-01
      • 2014-11-29
      • 2015-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多