微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

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

我正在尝试将 0 行插入具有唯一约束的表中,但我收到 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>表中有一条记录...

如果我在插入语句中添加 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

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

显然,从正在发生变化的表中进行选择是不正确的,必须修复。

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

解决方法

选择返回 0 行,没关系。

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

永远不要使用exception when others then return null!!!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。