【问题标题】:Oracle Equivalent to MySQL INSERT IGNORE?Oracle 相当于 MySQL INSERT IGNORE?
【发布时间】:2023-12-25 20:38:01
【问题描述】:

我需要更新一个查询,以便在插入之前检查是否存在重复条目。在 MySQL 中,我可以只使用 INSERT IGNORE,这样如果发现重复记录,它就会跳过插入,但我似乎找不到 Oracle 的等效选项。有什么建议吗?

【问题讨论】:

标签: oracle insert duplicates


【解决方案1】:

如果您使用的是 11g,则可以使用提示 IGNORE_ROW_ON_DUPKEY_INDEX

SQL> create table my_table(a number, constraint my_table_pk primary key (a));

Table created.

SQL> insert /*+ ignore_row_on_dupkey_index(my_table, my_table_pk) */
  2  into my_table
  3  select 1 from dual
  4  union all
  5  select 1 from dual;

1 row created.

【讨论】:

  • 感谢这工作!但似乎是一个非标准的解决方案......就像有人会记住这个符号一样。
  • @SonicSoul 你是对的,这是一种不寻常的编码方式。该手册甚至指出,提示通常没有语义效果。像所有提示一样,这个需要小心使用。例如,如果提示拼写错误,则不会出现编译时错误,该功能将默默地不起作用。
  • 是的,我最近接触了 Oracle 开发,并且对使用 sql 实现我以前认为简单的事情的这些技巧的数量感到非常困惑 :)
  • 根据This blog 的说法,尽管对于性能来说,这似乎不是一个好主意
【解决方案2】:

查看 MERGE 语句。这应该可以满足您的要求 - 这是 WHEN NOT MATCHED 子句将执行此操作。

由于 Oracle 不支持真正的 VALUES() 子句,具有固定值的单个记录的语法非常笨拙:

MERGE INTO your_table yt
USING (
   SELECT 42 as the_pk_value, 
          'some_value' as some_column
   FROM dual
) t on (yt.pk = t.the_pke_value) 
WHEN NOT MATCHED THEN 
   INSERT (pk, the_column)
   VALUES (t.the_pk_value, t.some_column);

另一种方法(例如,如果您要从不同的表进行批量加载)是使用 Oracle 的“错误日志记录”工具。语句如下所示:

 INSERT INTO your_table (col1, col2, col3)
 SELECT c1, c2, c3
 FROM staging_table
 LOG ERRORS INTO errlog ('some comment') REJECT LIMIT UNLIMITED;

之后所有可能引发错误的行都可以在表errlog 中找到。在使用 DBMS_ERRLOG.CREATE_ERROR_LOG 运行插入之前,您需要手动创建 errlog 表(或您选择的任何名称)。

详见说明书

【讨论】:

  • 日志错误仍然会在唯一键或主键违规时引发异常,除非事情发生了变化:asktom.oracle.com/pls/apex/…
  • @ShannonSeverance:它确实适用于 INSERTs,这是 Bad Programmer 所要求的。但是感谢链接,我不知道它不适用于 PK 的更新(我只将它用于插入)
  • 不应该将FROM 关键字替换为USING 关键字吗?如果需要,请查看*.com/questions/16414747/…
  • 仅供参考,您还可以使用一个 MERGE INTO 语句复制多行。您可以插入 WHEN NOT MATCHED 并可选择更新 WHEN MATCHED。
【解决方案3】:

我认为除了节省时间之外,您可以尝试插入并忽略不可避免的错误:

begin

   insert into table_a( col1, col2, col3 )
   values ( 1, 2, 3 );

   exception when dup_val_on_index then 
      null;

end;
/

这只会忽略重复主键或唯一键约束特别引发的异常;其他一切都会照常进行。

如果您不想这样做,则必须先从表格中进行选择,这实际上效率不高。

【讨论】:

  • 用 jdbc 尝试过,即使违反了 pk,executeUpdate() 返回了1,我希望它返回 0,因为没有更新任何行。这使我无法理解在使用此 for 插入时是否插入了新数据。
  • 如果您希望从辅助编程语言@AlikElzin 中识别 PK 违规,这对您来说是个错误的问题。只需执行INSERT 并以您使用的任何语言捕获任何异常。这个问题的每个答案都会返回1
【解决方案4】:

另一种变体

Insert into my_table (student_id, group_id)
select distinct p.studentid, g.groupid 
from person p, group g
where NOT EXISTS (select 1
                 from my_table a
                 where a.student_id = p.studentid
                 and a.group_id = g.groupid)

或者你可以这样做

Insert into my_table (student_id, group_id)
select distinct p.studentid, g.groupid 
from person p, group g
MINUS
select student_id, group_id
from my_table 

【讨论】:

    【解决方案5】:

    一个简单的解决方案

    insert into t1
      select from t2 
      where not exists 
        (select 1 from t1 where t1.id= t2.id)
    

    【讨论】:

      【解决方案6】:

      这不是我的,但在使用 sqlloader 时非常方便:

      1. 创建一个指向您的表的视图:

        CREATE OR REPLACE VIEW test_view
        AS SELECT * FROM test_tab
        
      2. 创建触发器:

        CREATE OR REPLACE TRIGGER test_trig
         INSTEAD OF INSERT ON test_view
         FOR EACH ROW
          BEGIN
           INSERT INTO test_tab VALUES
            (:NEW.id, :NEW.name);
          EXCEPTION
           WHEN DUP_VAL_ON_INDEX THEN NULL;
          END test_trig;
        
      3. 并在 ctl 文件中,改为插入到视图中:

        OPTIONS(ERRORS=0)
        LOAD DATA
        INFILE 'file_with_duplicates.csv'
        INTO TABLE test_view
        FIELDS TERMINATED BY ','
        (id, field1)
        

      【讨论】:

        【解决方案7】:

        如何简单地添加一个索引,其中包含您需要检查是否存在欺骗的任何字段并说它必须是唯一的?保存读取检查。

        【讨论】:

        • 因为如果有重复,插入会失败,而实际上你可能只想跳过它们。
        【解决方案8】:

        另一个“不存在的地方”——使用双重的变体......

        insert into t1(id, unique_name)
          select t1_seq.nextval, 'Franz-Xaver' from dual 
            where not exists (select 1 from t1 where unique_name = 'Franz-Xaver');
        

        【讨论】: