ORA-00001: 插入 0 行时违反了唯一约束

ORA-00001: unique constraint violated when inserting 0 rows

我正在尝试将 0 行插入到具有唯一约束的 table 中,但我收到 ORA-00001:违反了唯一约束...

下面是 PL/SQL 块,我希望能很好地解决问题

declare
  l_cnt number;
begin
  set transaction isolation level serializable;
  
  select count(*) into l_cnt from test_view;
  
  dbms_output.put_line('count = ' || to_char(l_cnt));
  
  insert into <table>(<columns>)
    select <columns> from test_view
    log errors ('run1')
    reject limit 0
  ;
  
  dbms_output.put_line('success');
  
exception
  when others then
    dbms_output.put_line('ERRROR!');
    dbms_output.put_line(sqlerrm);
    rollback;
end;
/

这是输出

count = 0
ERRROR!
ORA-00001: unique constraint (<SCHEMA>.<CONSTRAINT>) violated

果然ERR$_<table>table...

中有记录

如果我在插入语句中添加 where 1 = 0,一切正常,没有插入任何内容。

我仍然不相信我所看到的:)

Oracle Database 12c Standard Edition Release 12.2.0.1.0 - 64bit Production


更新 1

下面的示例未在视图上使用 count(*),因为这可能会导致不同的查询计划,即为插入选择所有必需的值。

declare
  l_cnt number;
begin
  set transaction isolation level serializable;
  
  insert into testx_table
  select * from testx_view d;
  
  select count(*) into l_cnt from testx_table;

  dbms_output.put_line('count = ' || to_char(l_cnt));

  insert into <table>(<columns>)
  select * from testx_view d
  log errors ('run2')
  reject limit 0;

  dbms_output.put_line('success');

exception
  when others then
    dbms_output.put_line('ERRROR!');
    dbms_output.put_line(sqlerrm);
    rollback;
end;
/

更新 2

我能够重现该行为。

CREATE TABLE A(ID NUMBER PRIMARY KEY)
/

CREATE FUNCTION F(ID_ NUMBER) RETURN NUMBER
AS
  L_ID NUMBER;
BEGIN
  SELECT ID INTO L_ID FROM A WHERE ID = ID_;
  RETURN L_ID;
EXCEPTION
  WHEN OTHERS THEN
    RETURN NULL;
END;
/

BEGIN
  DBMS_ERRLOG.CREATE_ERROR_LOG('A');
END;
/

BEGIN
  INSERT INTO A VALUES (1);
  
  INSERT INTO A SELECT 1 FROM DUAL WHERE F(1) IS NULL
  LOG ERRORS INTO ERR$_A;
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/

SELECT * FROM ERR$_A
/

sqlfiddle

这一切都归结为查询正在从函数内部修改的 table。函数抛出 ORA-04091: table A is mutating, trigger/function may not see it 但代码捕获所有异常和 returns null.

显然,从 table 中选择正在发生变化的是一个错误,必须加以修复。

而且我很生气,因为我数不清告诉我的同事停止使用 exception when others then return null 的次数。这又是一个完全掩盖问题的例子,我花了一整天时间调试它。

Select return编辑了 0 行,没问题。

然而,当向 table 插入行时,我们实际上是通过函数查询相同的 table。函数 returned ORA-04091: table A is mutating, trigger/function may not see it 但它在异常块中被捕获并且 null 被 returned。这导致查询 return 行...

切勿使用 exception when others then return null!!!