PL/SQL:检查触发器中的所有行后运行包

Posted

技术标签:

【中文标题】PL/SQL:检查触发器中的所有行后运行包【英文标题】:PL/SQL: Run package after checking all rows in trigger 【发布时间】:2020-02-14 17:39:12 【问题描述】:

我有一个包含预期数量和实际收到数量的库存表。假设inv.q_exinv.q_rd

表的 INSERT 在 q_ex 中具有正值,在 q_rd 中具有零值,因为它尚未到达。当我检测到 q_rd 值从 0 变为其他值时,我想运行一个包,表明它已被接收并存储。

在更新后触发检测并检查每一行很容易,但我不确定如何确保它只运行一次。

骨架是:

CREATE OR REPLACE TRIGGER example
     AFTER UPDATE ON inv
          FOR EACH ROW
               BEGIN
                    IF :OLD.q_rd = 0 AND :NEW.q_rd > 0 THEN
                         pkg.proc();
                    END IF;
               END;
/

我看到的问题是我只希望它运行一次。我只需要确定何时需要执行。理想情况下,在满足我的条件的第一行,我会退出循环(当我已经知道需要执行时继续检查似乎很浪费)并调用我的程序。

我找不到“退出”for each 并将其视为正常更新后的方法,因此我尝试同时使用更新前和更新后。 BEFORE 部分将检查每一行并更新一个布尔值。 AFTER 部分将等待该情况发生,如果为真,则调用该过程。

CREATE OR REPLACE TRIGGER example
     BEFORE UPDATE ON inv
          FOR EACH ROW
               DECLARE
                    shouldExecute BOOLEAN;
               BEGIN
                    IF :OLD.q_rd = 0 AND :NEW.q_rd > 0 THEN
                         shouldExecute := TRUE;
                    END IF;
               END;
     AFTER UPDATE ON inv
          BEGIN
               IF shouldExecute THEN
                    pkg.proc();
               END IF;
          END;
/

我怀疑这无论如何都行不通,因为根据语法,它重新声明了每一行的布尔变量。我认为也许我可以将其设为“全局”,但无论如何,由于某种原因,我无法将 BEFORE 和 AFTER 都添加到同一个触发器中(除非我没有进行足够的研究),所以我把它分成了两个触发器.现在的问题是我无法在两个触发器之间共享该布尔值。我可以分享价值,还是我做错了?

【问题讨论】:

【参考方案1】:

这里有很多小问题,所以我会尝试全部回答:)

关于“FOR EACH ROW”,Oracle 触发器支持两种不同的触发方法,STATEMENT 或 ROW。如果您在定义中包含“FOR EACH ROW”,您将获得一个行触发器,该触发器在受查询影响的每行触发一次,这似乎是您在这里想要的。对于每个查询,语句级触发器仅触发一次。使用行触发器的一个优点是您可以使用 :OLD 和 :NEW 元变量,它们引用先前和当前的行值。

正如您所发现的,您不能将 BEFORE 和 AFTER 添加到同一个触发器 - 您需要将它们分成两个触发器。

不幸的是,没有一种简单的方法可以在两个触发器之间共享布尔变量。最简单的方法可能是创建一个带有公共变量的包,您可以在 BEFORE 触发器中设置它,然后在 AFTER 触发器中检查。

包看起来像这样:

CREATE OR REPLACE PACKAGE PCKG_DEMO
AS
  shouldExecute BOOLEAN;
END;
/

然后你的 BEFORE UPDATE 触发器:

CREATE OR REPLACE TRIGGER beforeexample
     BEFORE UPDATE ON inv
          FOR EACH ROW
               BEGIN
                    IF :OLD.q_rd = 0 AND :NEW.q_rd > 0 THEN
                         PCKG_DEMO.shouldExecute := TRUE;
                    END IF;
               END;
/

还有你的 AFTER UPDATE 触发器:

CREATE OR REPLACE TRIGGER afterexample
     AFTER UPDATE ON inv
        FOR EACH ROW
          BEGIN
               IF PCKG_DEMO.shouldExecute THEN
                    pkg.proc();
               END IF;
          END;
/

这些有点伪代码 - 我现在无法访问 Oracle 数据库。您可以阅读有关触发器的更多信息here.希望这会有所帮助!

【讨论】:

好建议。我最终将该变量添加到程序所在的同一包中。谢谢! 你需要做更多的研究。上面的链接将带您引发有关 Oracle 9 的讨论。我希望您实际上并没有使用该版本;支持于 2007 年结束。自 11g 版以来,Oracle 提供了“复合触发器”,您可以在其中为语句和行级别触发之前和之后的相同触发器。复合触发器确实允许在不同调用之间共享变量。 我已经有一段时间没有使用触发器了。了解复合触发器很有用!为什么不把它写成答案呢?【参考方案2】:

你需要做更多的研究。上面的链接将带您引发有关 Oracle 9 的讨论。我希望您实际上并没有使用该版本;支持于 2007 年结束。自 11g 版以来,Oracle 提供了“复合触发器”,您可以在其中为语句和行级别触发之前和之后的相同触发器。复合触发器确实允许在不同调用之间共享变量。

【讨论】:

以上是关于PL/SQL:检查触发器中的所有行后运行包的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL 编程(三 )程序包和包体,触发器,视图,索引

PL/SQL:如何启用模式中的所有触发器?

PL/SQL 触发器在插入语句之前检查值

为啥 oracle 中的应用程序上下文使用 PL/SQL 过程

PL/SQL 创建一个根据日期范围检查 SYSDATE 的触发器

Oracle --- 存储过程函数包游标触发器