46

我需要能够运行一个 Oracle 查询来插入多行,但它还会检查主键是否存在,如果存在,则跳过该插入。就像是:

INSERT ALL
    IF NOT EXISTS( SELECT 1 WHERE fo.primary_key='bar' )
    (
        INSERT INTO 
            schema.myFoo fo ( primary_key, value1, value2 )
        VALUES
            ('bar','baz','bat')
    ),
    
    IF NOT EXISTS( SELECT 1 WHERE fo.primary_key='bar1' )
    (
        INSERT INTO 
            schema.myFoo fo ( primary_key, value1, value2 )
        VALUES
            ('bar1','baz1','bat1')
    )
SELECT * FROM schema.myFoo;

甲骨文有可能吗?

如果您能告诉我如何在 PostgreSQL 或 MySQL 中执行此操作,则可以加分。

4

10 回答 10

37

聚会迟到了,但是...

使用 oracle 11.2.0.1 有一个语义提示可以做到这一点:IGNORE_ROW_ON_DUPKEY_INDEX

例子:

insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(customer_orders,pk_customer_orders) */
  into customer_orders
       (order_id, customer, product)
values (    1234,     9876,  'K598')
     ;

更新:虽然这个提示有效(如果你拼写正确的话),有更好的方法不需要 Oracle 11R2:

第一种方法——上述语义提示的直接翻译:

begin
  insert into customer_orders
         (order_id, customer, product)
  values (    1234,     9876,  'K698')
  ;
  commit;
exception
  when DUP_VAL_ON_INDEX
  then ROLLBACK;
end;

第二种方法——当有很多争用时,比上述两种提示要快得多

begin
    select count (*)
    into   l_is_matching_row
    from   customer_orders
    where  order_id = 1234
    ;

    if (l_is_matching_row = 0)
    then
      insert into customer_orders
             (order_id, customer, product)
      values (    1234,     9876,  'K698')
      ;
      commit;
    end if;
exception
  when DUP_VAL_ON_INDEX
  then ROLLBACK;
end;
于 2011-02-25T08:30:38.333 回答
28

该语句称为 MERGE。看看吧,我太懒了。

但请注意,MERGE 不是原子的,这可能会导致以下效果(感谢 Marius):

SESS1:

create table t1 (pk int primary key, i int);
create table t11 (pk int primary key, i int);
insert into t1 values(1, 1);
insert into t11 values(2, 21);
insert into t11 values(3, 31);
commit;

SESS2:insert into t1 values(2, 2);

SESS1:

MERGE INTO t1 d
USING t11 s ON (d.pk = s.pk)
WHEN NOT MATCHED THEN INSERT (d.pk, d.i) VALUES (s.pk, s.i);

SESS2:commit;

SESS1:ORA-00001

于 2009-11-09T19:22:47.317 回答
18

仅当要插入的项目不存在时才插入。

工作方式相同:

if not exists (...) insert ... 

在 T-SQL 中

insert into destination (DESTINATIONABBREV) 
  select 'xyz' from dual 
  left outer join destination d on d.destinationabbrev = 'xyz' 
  where d.destinationid is null;

可能不漂亮,但很方便:)

于 2011-03-18T03:29:50.980 回答
17

我们可以结合DUALNOT EXISTS来满足您的要求:

INSERT INTO schema.myFoo ( 
    primary_key, value1, value2
) 
SELECT
    'bar', 'baz', 'bat' 
FROM DUAL
WHERE NOT EXISTS (
    SELECT 1 
    FROM schema.myFoo
    WHERE primary_key = 'bar'
);
于 2016-02-24T10:04:52.670 回答
15

如果您不想从其他表中合并,而是要插入新数据......我想出了这个。有没有更好的方法来做到这一点?

MERGE INTO TABLE1 a
    USING DUAL
    ON (a.C1_pk= 6)
WHEN NOT MATCHED THEN
    INSERT(C1_pk, C2,C3,C4)
    VALUES (6, 1,0,1);
于 2013-04-23T07:08:00.933 回答
5

如果该代码在客户端上,那么您需要多次访问服务器以消除这种情况。

将所有数据插入到一个临时表中,比如 T,其结构与 myFoo 相同

然后

insert myFoo
  select *
     from t
       where t.primary_key not in ( select primary_key from myFoo) 

这也应该适用于其他数据库 - 我已经在 Sybase 上完成了

如果要插入的新数据很少,因为您已经通过网络复制了所有数据,所以这不是最好的。

于 2009-11-09T18:48:40.357 回答
4
DECLARE
   tmp NUMBER(3,1);
BEGIN
  SELECT COUNT(content_id) INTO tmp FROM contents WHERE (condition);
  if tmp != 0 then
    INSERT INTO contents VALUES (...);
  else
    INSERT INTO contents VALUES (...);
  end if;
END;

我使用了上面的代码。它很长,但是很简单,对我有用。类似于 Micheal 的代码。

于 2012-06-13T13:27:16.133 回答
1

如果您的表与其他表“独立”(我的意思是,它不会触发级联删除或不会将任何外键关系设置为空),一个不错的技巧可能是首先删除该行然后再次插入它。它可以是这样的:

从 MyTable 中删除 prop1 = 'aaa'; //假设它将最多选择一行!

插入 MyTable (prop1, ...) VALUES ('aaa', ...);

如果您要删除不存在的内容,则不会发生任何事情。

于 2013-10-28T14:28:22.567 回答
0

这是对 erikkallen 发表的评论的回答:

您不需要临时表。如果你只有几行,(SELECT 1 FROM dual UNION SELECT 2 FROM dual) 就可以了。为什么你的例子会给出 ORA-0001?在 Sess1 提交或回滚之前,merge 不会在索引键上获取更新锁并且不会继续吗?– 埃里卡伦

好吧,你自己试试,告诉我你是否得到同样的错误:

SESS1:

create table t1 (pk int primary key, i int);
create table t11 (pk int primary key, i int);
insert into t1 values(1, 1);
insert into t11 values(2, 21);
insert into t11 values(3, 31);
commit;

SESS2:insert into t1 values(2, 2);

SESS1:

MERGE INTO t1 d
USING t11 s ON (d.pk = s.pk)
WHEN NOT MATCHED THEN INSERT (d.pk, d.i) VALUES (s.pk, s.i);

SESS2:commit;

SESS1:ORA-00001

于 2009-11-09T22:15:13.327 回答
0

插入到 schema.myFoo (primary_key, value1, value2)
                         SELECT 'bar1' AS primary_key ,'baz1' AS value1 ,'bat1' AS value2 FROM DUAL WHERE (SELECT 1 AS value FROM schema.myFoo WHERE LOWER(primary_key) ='bar1' AND ROWNUM=1) 为空;

于 2015-11-25T15:42:49.843 回答