【问题标题】:Package, triggers and procedure not working as they should包、触发器和程序无法正常工作
【发布时间】:2017-08-04 23:27:01
【问题描述】:

我需要一些帮助,因为我看不出这段代码的问题在哪里。

我有 2 张桌子。产品(id,..,currentPrice) 和价格(id, product_id, date, price)。我需要创建触发器和程序,当 Prices.price 中的值被插入、更新或删除时,它们将自动更新 Products.currentPrice 中的值。 我创建了一个包、两个触发器和一个过程,它们都可以编译,但是当我插入、更新或删除一个值时,什么也没有发生。 但是,当我手动调用过程并插入 product_id (例如 Products 表中现有产品的值 1 )时,Products.currentPrice 中 id 为 1 的产品的值每次都会更新。 我猜 BEFORE 触发器的值没有保存在 PACKAGE 变量中,因为我收到“找不到数据”错误。 我正在使用 SQL Developer 和 Oracle 11g XE。

这是我的代码:

CREATE OR REPLACE PACKAGE mypackage IS
gid NUMBER;
END;


CREATE OR REPLACE TRIGGER trg_currentprice1
BEFORE INSERT OR UPDATE OR DELETE ON prices
FOR EACH ROW
BEGIN
  IF (INSERTING OR UPDATING) THEN
    BEGIN
      mypackage.gid := :new.product_id;
    END;
  ELSE
    BEGIN
      mypackage.gid := :old.product_id;
    END;
  END IF;
END;


CREATE OR REPLACE TRIGGER trg_currentprice2
AFTER INSERT OR UPDATE OR DELETE ON prices
DECLARE
  v_id NUMBER := mypackage.gid;  
BEGIN
    calc_currentprice(v_id);
END;


CREATE OR REPLACE PROCEDURE calc_currentprice(prodid IN NUMBER) AS
  curprice Products.currentPrice%TYPE;
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  curprice := 0;
SELECT price INTO curprice FROM prices
WHERE product_id = prodid AND date = (SELECT MAX(date) FROM prices WHERE product_id = prodid AND date <= SYSDATE);
EXECUTE IMMEDIATE 'ALTER TRIGGER trg_products_forbid2 DISABLE';
UPDATE products
SET currentPrice = curprice
WHERE product_id = prodid;
EXECUTE IMMEDIATE 'ALTER TRIGGER trg_products_forbid2 ENABLE';
END;

【问题讨论】:

  • 同意 unleashed - 您不应该使用自主事务,也不应该在此处更改触发器。为什么不包括以下事实的众多原因之一:更改触发器是一项全局操作并影响所有会话,而不仅仅是当前会话。

标签: database oracle plsql triggers procedure


【解决方案1】:

您在这里做了几件令人担忧的事情。首先,您尝试使用两个触发器来执行一系列工作。触发器触发的顺序将无法保证。在一个触发器中考虑以下内容。

CREATE OR REPLACE TRIGGER trg_currentprice1
BEFORE INSERT OR UPDATE OR DELETE ON prices
FOR EACH ROW
BEGIN
  IF (INSERTING OR UPDATING) THEN
    BEGIN
      calc_currentprice(:new.product_id);
    END;
  ELSE
    BEGIN
      calc_currentprice(:old.product_id);
    END;
  END IF;
END;

其次,在您的过程中,您指定了一个自治事务,但您不提交。

现在,这些对您的代码有帮助,但您还应该了解其他一些事情。

我建议不要使用触发器运行它。如果您有更新价格的 PL/SQL 代码,则包含更新产品的代码。也许更好的是,您能否将价格与产品分离,这样您就不必更新产品记录,只需根据需要查找产品的当前价格。

不要在这样的触发器处理中包含提交。如果用户事务未提交,那么您提交的更改将不再有效,但仍将保留,因此您可以使用回滚后不存在的价格更新产品。

不要在代码中禁用/启用触发器。如果你必须这样做,那么你的设计就有缺陷。

您正在尝试在代码执行过程中使用全局变量。这也应该避免。

【讨论】:

  • 我是 PL/SQL 的新手,这段代码只是我为了学习而必须完成的更大任务的一小部分。我需要使用触发器和程序。我已经阅读了您告诉我的内容,但是因为我是 Oracle PL/SQL 的“菜鸟”,所以我没有很好地理解它。如果我说得对,我应该添加 COMMIT;在第二次自主交易之后...
  • 实际上,您应该从该过程中删除 PRAGMA AUTONOMOUS TRANSACTION,并且仅在您的其他外部过程或过程调用之一中将您的事务作为正常事务提交。
  • 但如果删除 PRAGMA 则我无法更新 Products 中的 currentPrice 字段,因为我有强制触发器禁止更改该值。我可以将 PRAGMA 移动到 AFTER 触发器中,该触发器将调用此过程。但令我难过的是,她自己的程序可以工作,但结合包和 2 触发器它没有,我可以看到一个解决方案。
  • 啊哈!因为 DDL 会导致提交。但是,什么是非常不清楚的。您想要更新一个值,您有一个触发器应该阻止您更新所述值。你有截然相反的要求。此外,正如我之前提到的,您不希望在现实世界代码中的触发器中发生提交,因为外部事务可能会回滚。
猜你喜欢
  • 1970-01-01
  • 2012-02-25
  • 2015-12-02
  • 2012-01-04
  • 1970-01-01
  • 2019-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多