PL/SQL:检查触发器中的所有行后运行包
Posted
技术标签:
【中文标题】PL/SQL:检查触发器中的所有行后运行包【英文标题】:PL/SQL: Run package after checking all rows in trigger 【发布时间】:2020-02-14 17:39:12 【问题描述】:我有一个包含预期数量和实际收到数量的库存表。假设inv.q_ex
和inv.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:检查触发器中的所有行后运行包的主要内容,如果未能解决你的问题,请参考以下文章
为啥 oracle 中的应用程序上下文使用 PL/SQL 过程