在更新触发之前执行时出现约束冲突错误

Posted

技术标签:

【中文标题】在更新触发之前执行时出现约束冲突错误【英文标题】:Constraint violation error while executing before update trigger 【发布时间】:2013-12-02 10:54:35 【问题描述】:

我正在使用 oracle 提供的示例 HR 模式试验触发器。 每当使用触发器在 DEPARTMENTS TABLE 中更新或删除他们各自的部门 ID 时,我都会尝试删除或更新所有员工。

这是代码:

create or replace trigger UPDATE_EMPLOYEES_DEPT_ID
before update or delete of DEPARTMENT_ID on DEPARTMENTS
for each row
begin
  if UPDATING then
   update EMPLOYEES set DEPARTMENT_ID = :new.DEPARTMENT_ID where DEPARTMENT_ID = :OLD.DEPARTMENT_ID;
  ELSIF DELETING then
   update employees set department_id = null where DEPARTMENT_ID = :OLD.DEPARTMENT_ID;
  end if;

end;

当我执行时:

UPDATE departments SET department_ID = 112 WHERE department_ID = 110;

它给了我违反约束的错误。

Error report:
SQL Error: ORA-02292: integrity constraint (HR.JHIST_DEPT_FK) violated - child record found
02292. 00000 - "integrity constraint (%s.%s) violated - child record found"
*Cause:    attempted to delete a parent key value that had a foreign
       dependency.
*Action:   delete dependencies first then parent or disable constraint.

我哪里出错了?是否在“BEFORE TRIGGER”之前检查完整性约束?

【问题讨论】:

更新主键是一个非常糟糕的主意,不应该这样做。当您尝试更新在其他表中引用的键时,错误很明显(外键约束)。 @AbhijithNagarajan 删除呢? @Neal 如果您只想传播删除,请在外键定义上使用 ON DELETE UPDATE @Neal 如果您想显示以前的数据怎么办?通过编辑关键信息,您会丢失参考资料。除非您将其存储在其他表中,例如历史表或类似的东西,否则您将无法再显示以前的数据。例如,您想知道您的员工过去属于哪些部门。但这只是我的一个想法,在你的情况下可能不需要它 【参考方案1】:

更新主键是probably not a good idea.。如果您绝对必须这样做,我建议您不要使用触发器。在触发器中包含业务规则往往会导致难以维护应用程序,因为代码是隐藏/碎片化的,并且它使标准 DML 看起来像魔术(意外的副作用)。

话虽如此,但在您的情况下,您的错误似乎来自另一个表中的外键(可能是JOB_HISTORY?),并且您的代码逻辑在大多数情况下应该可以工作,只要您更新所有引用的所有子记录表格。

【讨论】:

【参考方案2】:

实现你想要的东西是相当有问题的,但并非不可能。

这里的问题是,在将键设置为 null 之前,您必须处理任何可能的约束。

我相信如果我们想实现一个解决方案,我们需要在这里使用一点动态 SQL。

这是我快速调整的触发器的修改版本(注意:我没有测试过):

create or replace trigger UPDATE_EMPLOYEES_DEPT_ID
before update or delete of DEPARTMENT_ID on DEPARTMENTS
 for each row
begin
  if UPDATING then
   update EMPLOYEES set DEPARTMENT_ID = :new.DEPARTMENT_ID where DEPARTMENT_ID = :OLD.DEPARTMENT_ID;
  elsif DELETING then
   for i in ( select CONSTRAINT_NAME from USER_CONSTRAINTS where CONSTRAINT_TYPE='R' and R_CONSTRAINT_NAME in (select CONSTRAINT_NAME from USER_CONSTRAINTS where CONSTRAINT_TYPE in ('P','U') and TABLE_NAME='EMPLOYEES') )
    loop
     for j in ( select TABLE_NAME, COLUMN_NAME from USER_CONS_COLUMNS where CONSTRAINT_NAME = i.CONSTRAINT_NAME )
      loop
       update j.TABLE_NAME set j.COLUMN_NAME = null where j.COLUMN_NAME = :OLD.DEPARTMENT_ID;
      end loop;
    end loop;
   update EMPLOYEES set DEPARTMENT_ID = null where DEPARTMENT_ID = :OLD.DEPARTMENT_ID;
  end if;
end;

让我解释一下删除的工作原理:

首先它获取所有引用表“EMPLOYEES”的外键 然后对于找到的每个外键,它都会抓取它引用的表名和列 然后,对于每个表-列对,它会将匹配 :OLD.DEPARTMENT_ID 的那些字段的值清空 最后,在处理完所有外键后,它会将 EMPLOYEES 表中的 DEPARTMENT_ID 清空,正如您最初计划的那样。

由于我还没有真正测试过,如果这会导致任何问题,请告诉我。

【讨论】:

幸运的是这行不通!您真的不想盲目地更新子记录。这也不是动态 SQL 的工作方式。【参考方案3】:

如果你想使用触发器,那么你可以删除约束,

I mean, perform all the operation of update delete by trigger only. and remove the constraint.
but doing so is not a good practice. So be sure with whatever you want to do.

【讨论】:

以上是关于在更新触发之前执行时出现约束冲突错误的主要内容,如果未能解决你的问题,请参考以下文章

尝试编写 MySQL 触发器以替换 phpmyadmin 上的 CHECK 约束时出现语法错误

在配置触发器时更新表中的列时出现错误

Oracle - 在没有触发器的情况下更新表时出现“表正在变异,触发器/函数可能看不到它”错误

执行全新安装 Piranha CMS 时出现外键约束错误

更新触发器后创建空字符串时出现无法解释的 MySQL 错误 #1064?

约束是在自定义触发器之前还是之后执行的?