如何绕过唯一约束违规?

Posted

技术标签:

【中文标题】如何绕过唯一约束违规?【英文标题】:How to get around a unique constraint violation? 【发布时间】:2021-10-10 08:26:24 【问题描述】:

我有一个问题,我无法实现的自治事务将尝试将数据插入表中,但数据可能已经存在。我们需要找到一种解决方案来处理唯一完整性约束,而不需要移除约束,因为另一个程序依赖于这个约束验证。

我尝试创建一个删除现有条目的前触发器,但这似乎不起作用。

CREATE OR REPLACE TRIGGER TG_BEFORE_INSERT_REPLACE_DC_DEVICE_TACTICAL_FIX_INV_LOADER
BEFORE INSERT
ON DC_DEVICE
FOR EACH ROW
DECLARE
    PRAGMA AUTONOMOUS_TRANSACTION;
    FAKE_DEVICE_STATUS VARCHAR2(3 BYTE);
BEGIN
    -- constraint exception occurs before this statement
    DELETE FROM DC_DEVICE WHERE DEVICE_ID_PK = :NEW.DEVICE_ID_PK;
END;

在那之后,我想到了删除约束并添加带有条件语句的触发器,来自损坏程序的所有条目都是不同的,所以我可以区分它们。使用该语句,如果设备已经存在并且插入不是来自损坏的程序,则无法继续,否则删除现有条目并继续插入。

上面的解决方案似乎是一个可行的解决方案,但我的问题是,是否有任何功能可以让我以更优雅的方式做同样的事情?

【问题讨论】:

这似乎是应用程序插入数据的问题,它应该使用合并或处理违反唯一约束。无论如何,一个想法是重命名表并在其前面放置一个视图,然后您可以添加而不是触发器将违规行放入异常表中。 您想对您尝试插入的违反约束的记录做什么?你想保留它们,你想替换现有的? 我想替换记录,但我无法更改应用程序的查询,我们无法控制代码,但我们必须找到解决方案... 您如何区分这些记录?您可能希望使用基于函数的索引来创建条件唯一约束。 如果设备状态 != 'FKE' 那么 device_id 应该是唯一的吗?如果是这样,您可以使用基于函数的索引来实现。 【参考方案1】:

一种选择是使用instead of 触发器。但是,此解决方案要求您重命名表并使用它们的名称创建视图。这样您不会影响应用程序逻辑,但它可能会影响整体性能,因此应正确测试。

不过,使用触发器来更改错误的应用程序逻辑并不是一个好主意。我理解您的困境,即有时我们需要找到解决现有问题的方法,但这并不能解决问题。

无论如何,下面是一个您可以应用于您的逻辑的简单示例

SQL> create table t ( c1 number primary key , c2 varchar2(1) ) ;

Table created.

SQL> alter table t rename to tbl_t ;

Table altered.

SQL>  create view t as ( select c1 , c2 from tbl_t ) ;

View created.

现在我们创建一个instead of 触发器

SQL> create or replace trigger tr_v_t
  2  instead of insert
  3  on t
  4  for each row
  5  declare
  6    pk_violation_exception exception;
  7    pragma exception_init(pk_violation_exception, -00001);
  8  begin
  9    insert into tbl_t (c1,c2)
 10    values ( :new.c1,:new.c2 );
 11    exception
 12      when pk_violation_exception then
 13        dbms_output.put_line('ora-00001 (pk_violation_exception) captured');
 14        update tbl_t
 15        set c2   = :new.c2
 16        where c1 = :new.c1 ;
 17* end;
SQL> /

Trigger created.

使用此触发器,任何违反约束的尝试都会使最终表中的值更新成为可能。

SQL> select * from t ;

no rows selected

SQL> insert into t values ( 1 , 'A' ) ;

1 row created.

SQL> commit ;

Commit complete.

SQL> insert into t values ( 2, 'B' ) ;

1 row created.

SQL> commit ;

Commit complete.

SQL> insert into t values ( 2, 'C' ) ;
ORA-00001 (pk_violation_exception) captured

1 row created.

SQL> select * from tbl_t ;

        C1 C
---------- -
         1 A
         2 C

【讨论】:

我 100% 同意您的说法,但我无能为力,感谢您的回答,我会尝试并在成功后将其作为官方答案 这在我的情况下并不完全有效,但它是一个非常好的选择,谢谢!

以上是关于如何绕过唯一约束违规?的主要内容,如果未能解决你的问题,请参考以下文章

具有唯一索引和主键的列给出唯一约束违规

在 Hibernate 中使用 SEQUENCE 时导致唯一约束违规的原因是啥?

SELECT查询在Oracle中给出唯一约束违规

通过实体框架更新时如何绕过唯一键约束(使用 dbcontext.SaveChanges())

具有序列提供的 id 的主键列上的唯一约束违规

HSQLDB:奇怪的“唯一约束或索引违规”,从 CSV 读取数据