我正在使用Oracle Express 12c。我创建的其中一个表有一个关联的触发器,可以防止它直接更新其中一个列。但即使另一个应该具有此类访问权限的表尝试执行此操作,它也会触发。
例如:
我有表A和B,B有一个将它链接到A的外键。我故意将一个属性从A添加到B.一个触发器,我们称之为UPD_FROM_B,阻止B更新该属性。另一个UPD_FROM_A应该在B上更新此属性,如果它在A上更新。现在UPD_FROM_B阻止UPD_FROM_A做它应该做的事情。
或通过一个工作示例:
有两个表:客户和订单。 客户可以有多个订单,但一个订单只有一个客户。为了项目,我不得不在订单上加上 customer_name ,即使每个订单都有 customer_id 作为外键。
一次触发 - UPD_NAME_ORDER 阻止订单更新 每当 customer_name 更新时, customer_name ,另一个 - UPD_NAME_CUST 会在订单表的相应行中更新此列在客户
如何确定哪个表触发了操作并允许UPDATE为一个,但仍然阻止了另一个?
答案 0 :(得分:2)
我认为您必须仅更改触发器UPD_FROM_B。 首先,当父键和外键相等时,从表A中选择列的值,然后将此值与表B中的列值进行比较。如果此值等于您的触发器,则允许执行此更新,否则不会。您编写此代码如下:
CREATE TRIGGER UPD_FROM_B before update on B
DECLARE
val A.upd_column%TYPE;
BEGIN
select A.upd_column into val
where A.ID=B.FKID
if val=B.upd_column then
RAISE;
else ......
end if;
END;
答案 1 :(得分:1)
从表面上看,我知道这样做的方法是使用包变量作为门键并在2个触发器之间共享它。触发器A将在嵌套更新B之前设置状态变量。触发器B将检查A是否设置var,如果是,则更新成功,否则,B知道A不是调用者,它应该阻止更新。
此外,我假设您的目的是实现“ UPDATE CASCADE ”触发器,以根据父更新更新子记录外键值,同时在更新FK值时保留关系。如果是这样,你必须小心这种方法,它只有在你不允许多行更新时才能正常工作。
首先是一个包和状态var:
CREATE PACKAGE IsUpdating IS
A number;
END;
在触发器A的顶部执行类似下面的操作。异常处理程序是一个“finally”块,它总是执行以避免在更新错误的情况下将包变量保留为无效状态:
CREATE TRIGGER A_UPD_CASCADE after update on A for each row
BEGIN
IsUpdating.A := 1;
update B set B.FKID = :new.FKID WHERE B.FKID = :old.FKID;
IsUpdating.A := 0;
EXCEPTION
WHEN OTHERS
THEN
IsUpdating.A := 0;
RAISE;
END;
内部触发器B执行此操作:
CREATE TRIGGER B_UPD_CASCADE before update on B
BEGIN
if IsUpdating.A != 1 then
-- Disallow update since it is coming from B alone
RAISE;
end if;
END;
CASCADE UPDATE的缺陷是在单个语句中使用多行父更新,Oracle将为每个父值执行触发器,导致某些子值根据值前后链接多次更新。