【问题标题】:Oracle database - determine what table triggered a triggerOracle 数据库 - 确定哪个表触发了触发器
【发布时间】:2017-05-20 14:01:58
【问题描述】:

我正在使用 Oracle Express 12c。我创建的一个表有一个关联的触发器,它阻止它直接更新它的列之一。但即使当另一个应该具有这种访问权限的表尝试执行此操作时,它也会触发。

例如:

我有表 A 和 B,B 有一个将其链接到 A 的外键。我故意将其中一个属性从 A 添加到 B。一个触发器,我们称之为 UPD_FROM_B,阻止 B 更新此属性。另一个 UPD_FROM_A 应该在 B 上更新此属性,如果它在 A 上更新。现在 UPD_FROM_B 阻止 UPD_FROM_A 做它应该做的事情。


或者通过一个工作示例:

有两个表:customerorder客户可以有多个订单,但一个订单只有一个客户。为了项目,我不得不把 customer_name 放在订单上,即使每个订单都有 customer_id 作为外键。

一个触发器 - UPD_NAME_ORDER 阻止 order 更新 customer_name 和另一个 - UPD_NAME_CUSTcustomer_name 更新时更新 order 表的相应行中的此列在客户


如何确定哪个表触发了该操作并允许对其中一个表执行 UPDATE,但仍阻止另一个表执行?

【问题讨论】:

  • 当你说一个表“试图做什么”是什么意思?你的意思是在另一个表上可能有另一个触发器试图更新你的表?请尝试发布minimal reproducible example 以帮助人们了解您的需求并给您答复,
  • 我相信你无法确定是谁触发了你的触发器,但也许你会发现一些有趣的东西here
  • 您的“示例”不清楚。哪个表拥有哪个触发器?基于对触发器如何工作的误解,您的示例听起来像是您正在考虑的假设;不是现有的场景。如果没有,则发布 DDL 或查询目录视图 ALL_TRIGGERS 以找出答案。
  • @mustaccio - 虽然我同意你在他的问题背景下的陈述,但你的陈述并不完全正确。 Oracle 支持特定列上的触发器以及使用触发器的 ON 和 WHEN 子句的更细粒度的强制逻辑,因此一个 UPDATE 可能会通过,而另一个可能不会。
  • @mustaccio - 来吧。如果你要迂腐和讽刺,至少是正确的。在指出您的错误陈述时,我试图保持礼貌。我所说的“上下文”的意思更多的是“出于他的问题的目的”,承认您的错误评论仍然适用于他。对不起,你有防守,但它仍然是不正确的。语句如果你有一个阻止更新语句的触发器,所有的更新语句都会被它阻止,这仍然是错误的。

标签: sql oracle plsql triggers oracle12c


【解决方案1】:

我认为您必须仅更改触发器 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;

【讨论】:

    【解决方案2】:

    从表面上看,我知道这样做的方法是使用包变量作为门键并在 2 个触发器之间共享它。触发器 A 将在其对 B 的嵌套更新之前设置状态变量。触发器 B 将检查 A 是否设置了 var,如果是,则更新成功,如果没有,则 B 知道 A 不是调用者,它应该阻止更新。

    另外,我假设您的意图是实现“UPDATE CASCADE”触发器以根据父更新更新子记录外键值,在更新 FK 值时保留关系。如果是这样,你必须小心这种方法,它只有在你禁止多行更新时才能正常工作。

    首先是一个包和状态变量:

    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 将为每个父值执行触发器,导致某些子值根据链接的前后值多次更新。

    【讨论】:

    • 我不会实施 UPDATE CASCADE,但我相信这应该可行。无论如何,我会听取您的建议并为我的问题添加一个更合适的示例。我很快就会对此进行测试,因此我可以将您的答案标记为已接受。非常感谢。
    猜你喜欢
    • 2021-07-14
    • 2013-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-07
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    相关资源
    最近更新 更多