我有一个具有唯一约束的数据库表(唯一的(DADSNBR, DAROLEID)对)。 我将同时向该表中插入多个值,因此我想使用一个查询来完成它-我假设这将是更快的方法。 因此,我的查询是:
INSERT ALL
INTO ACCESS (DADSNBR, DAROLEID) VALUES (68, 1)
INTO ACCESS (DADSNBR, DAROLEID) VALUES (68, 2)
INTO ACCESS (DADSNBR, DAROLEID) VALUES (68, 3)
INTO ACCESS (DADSNBR, DAROLEID) VALUES (68, 4)
SELECT 1 FROM DUAL
由于语句中的某些条目与数据库中已有的条目重复,因此整个插入操作将失败,并且不会插入任何行。
有没有办法忽略唯一约束失败的情况,而只插入唯一约束,而不必将其拆分为单独的INSERT语句?
编辑:我意识到我可能仍然不想这样做,但是我仍然对是否可行感到好奇。
您所有的行都不是重复的(?!)
我的意思是,它们是执行插入语句之前数据库中已经存在的值的重复项。 为了清楚起见,编辑了我的问题。
在Oracle中,语句要么完全成功要么完全失败(它们是原子的)。 但是,您可以在某些情况下添加子句以记录异常,而不是引发错误:
使用BULK COLLECT - SAVE EXCEPTIONS,如askTom上的该线程所示,
或使用DBMS_ERRLOG(我认为自10g起可用)。
第二种方法是全自动的,这是一个演示(使用11gR2):
SQL> CREATE TABLE test (pk1 NUMBER,
2 pk2 NUMBER,
3 CONSTRAINT pk_test PRIMARY KEY (pk1, pk2));
Table created.
SQL> /* Statement fails because of duplicate */
SQL> INSERT into test (SELECT 1, 1 FROM dual CONNECT BY LEVEL <= 2);
ERROR at line 1:
ORA-00001: unique constraint (VNZ.PK_TEST) violated
SQL> BEGIN dbms_errlog.create_error_log('TEST'); END;
2 /
PL/SQL procedure successfully completed.
SQL> /* Statement succeeds and the error will be logged */
SQL> INSERT into test (SELECT 1, 1 FROM dual CONNECT BY LEVEL <= 2)
2 LOG ERRORS REJECT LIMIT UNLIMITED;
1 row(s) inserted.
SQL> select ORA_ERR_MESG$, pk1, pk2 from err$_test;
ORA_ERR_MESG$ PK1 PK2
--------------------------------------------------- --- ---
ORA-00001: unique constraint (VNZ.PK_TEST) violated 1 1
您可以将LOG ERROR子句与INSERT ALL一起使用(感谢@Alex Poole),但是必须在每个表之后添加该子句:
SQL> INSERT ALL
2 INTO test VALUES (1, 1) LOG ERRORS REJECT LIMIT UNLIMITED
3 INTO test VALUES (1, 1) LOG ERRORS REJECT LIMIT UNLIMITED
4 (SELECT * FROM dual);
0 row(s) inserted.
它确实适用于INSERT ALL,但是您需要在每个INTO ...之后放置一个LOG ERRORS子句,如文档中斜一点所示。因此,在这种情况下INSERT ALL INTO test VALUES (1, 1) LOG ERRORS REJECT LIMIT UNLIMITED INTO test VALUES (1, 1) LOG ERRORS REJECT LIMIT UNLIMITED SELECT * FROM dual。这可能很有意义,因为INTO可能适用于不同的表。
@AlexPoole谢谢,我不知道。这说得通 !
DBMS_ERRLOG很好用,我不必更改查询结构-感谢分享!但是,有一件事是,因为记录错误而不是首先避免这些错误,因为使用这种方法要谨慎一些,因为它会占用大量空间/资源吗?
@Maccath涉及额外的工作。如果您运行大量插入操作并且您对日志不感兴趣,则最好按照@a_horse_with_no_names答案重写查询。
使用MERGE语句来处理这种情况:
merge into"ACCESS" a
using
(
select 68 as DADSNBR,1 as DAROLEID from dual union all
select 68,2 from dual union all
select 68,3 from dual union all
select 68,4 from dual
) t
on (t.DADSNBR = a.DADSNBR and t.DAROLEID = a.DAROLEID)
when not matched then
insert (DADSNBR, DAROLEID)
values (t.DADSNBR, t.DAROLEID);
嗨,我尝试使用以下查询,但出现错误。我无法立即看到问题出在哪里,但是我还没有注意到Oracle。 ORA-00928: missing SELECT keyword: merge into DADSNBR using ( select :q0 as DADSNBR, :q1 as DAROLEID from dual union all ) t on (t.DADSNBR = access.DADSNBR and t.DAROLEID = access.DAROLEID) when not matched then insert (DADSNBR, DAROLEID) values (t.DADSNBR, t.DAROLEID)我将查询中的第二个DADSNBR更改为DAROLEID,因为那是我假设的意思,但这并不能解决。 :(
@Maccath-union all将多个select语句(在本示例中为四个,如您的原始问题)组合为一个结果集。您的版本的结尾为union all;在最后一个select子语句之后不应出现。
该脚本太混乱了,您将DADSNBR用作列名和表名两次,然后您的匹配子句将其两次。.令我惊讶的是,即使您对此进行了测试,Oracle甚至都没有抛出错误。最后,您的查询没有回答问题。最后,如果要使用正确的表名作为OP,则此语句仍不会阻止将重复项插入到该表中,而只是从源而不是从目标中删除。
@sksallaj:感谢您指出我的错别字,我已经纠正了。它确实回答了这个问题,因为它可以防止使用单个语句插入重复项。因为如果有行,则MERGE将具有"匹配项",但是由于没有WHEN MATCHED子句,因此将不会发生任何事情。因此,仅将不存在的行插入到目标表中。在这里查看我的示例:sqlfiddle.com/#!4/79c66/3
好吧,我还给你投票:)