在更新触发之前执行时出现约束冲突错误
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 - 在没有触发器的情况下更新表时出现“表正在变异,触发器/函数可能看不到它”错误