【问题标题】:Oracle PL/SQL trigger that updates a different table更新不同表的 Oracle PL/SQL 触发器
【发布时间】:2017-10-14 19:15:07
【问题描述】:

我正在为大学做这个练习,我已经被困了一个星期。我需要创建一个触发器,以便在更改表“tbvale”上的状态列时,相应地在“tbfuncionario”列上发生一些更新。 我意识到我的代码可能看起来很麻烦,甚至可能不合适,但它是一个旨在教授特定事物的单一练习。 到目前为止,这就是我想出的:

CREATE OR REPLACE TRIGGER status_chg
AFTER UPDATE OF status ON tbvale
FOR EACH ROW
DECLARE
    nvd tbfuncionario.numvalesdescontados%type;
    nva tbfuncionario.numvalesaberto%type;
    vtva tbfuncionario.valortotalvalesaberto%type;
    nve tbfuncionario.numvalesemitidos%type;
    vv tbvale.valorvale%type;
    cod tbvale.fkcodmat%type;
    pragma autonomous_transaction;
BEGIN
    IF (:NEW.status <> :OLD.status) THEN
        SELECT valorvale INTO vv FROM tbvale;
        SELECT fkcodmat INTO cod FROM tbvale;
        IF (:NEW.status = 2) THEN
            nvd := 1;
            nva := -1;
            nve := 0;
            vv := vv - 1;
        ELSE
            nvd := 0;
            nve := 1;
            nva := 1;
            vv := vv + 1;
        END IF;
        UPDATE tbfuncionario
        SET numvalesdescontados = numvalesdescontados + nvd,
            numvalesaberto = numvalesaberto + nva,
            numvalesemitidos = numvalesemitidos + nve,
            valortotalvalesaberto = valortotalvalesaberto + vv
        WHERE pkcodmat = cod;
    END IF;
END;

pkcodmat 是 tbfuncionario 的 PK,fkcodmat 是 tbvale 引用 pkcodmat 的 FK。

目前我跑步时

UPDATE tbvale SET STATUS = 2 WHERE PKCODVALE = 3;

我收到以下错误:

ERROR: ORA-01422: exact fetch returns more than requested 
number of rows ORA-06512: at "ULBRA.STATUS_CHG", line 11 
ORA-04088: error during execution of trigger 'ULBRA.STATUS_CHG' 

Error Code: 1422

Query = UPDATE tbvale SET STATUS = 2 WHERE PKCODVALE = 3

我不明白为什么它会“返回超过请求的行数”,因为只有一行 pkcodmat 等于 fkcodmat。

我感谢任何关于为什么会发生这种情况以及如何解决它的见解。

【问题讨论】:

  • 在触发器主体的 select 语句中添加 where 子句。
  • 这些查询必须返回单行:SELECT valorvale INTO vv FROM tbvale;SELECT fkcodmat INTO cod FROM tbvale;
  • "'我已经卡了一个星期" 如果你自己运行SELECT valorvale FROM tbvale;,你可以节省六天二十三小时五十九天的卡住。 `

标签: sql oracle plsql triggers


【解决方案1】:

触发器看起来不错。以下是几点:

  1. IF (:NEW.status &lt;&gt; :OLD.status) 不会检测到更改形式和为 NULL,以防列可以为空。我不会在正文中使用IF,而是使用触发器WHEN 子句。
  2. 您正在为变量使用列类型。然而,这些应该只保存整数。因此不需要列类型。
  3. SELECT valorvale INTO vv FROM tbvale,这不起作用。你想要一行的值,所以你需要一个WHERE 子句。但由于您应该对当前记录值感兴趣,您可以简单地使用:new.valorvale
  4. SELECT fkcodmat INTO cod FROM tbvale 也一样。
  5. 自主触发器最后需要COMMITROLLBACK

这里是更正的触发器:

CREATE OR REPLACE TRIGGER status_chg
AFTER UPDATE OF status ON tbvale
FOR EACH ROW
WHEN (DECODE(NEW.STATUS, OLD.STATUS, 0, 1) = 1)
DECLARE
  v_vd integer;
  v_va integer;
  v_ve integer;
  v_vv tbvale.valorvale%type;
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  IF (:NEW.status = 2) THEN
    v_vd := 1;
    v_va := -1;
    v_ve := 0;
    v_vv := :NEW.valorvale - 1;
  ELSE
    v_vd := 0;
    v_va := 1;
    v_ve := 1;
    v_vv := :NEW.valorvale + 1;
  END IF;

  UPDATE tbfuncionario
  SET numvalesdescontados = numvalesdescontados + v_vd,
      numvalesaberto = numvalesaberto + v_va,
      numvalesemitidos = numvalesemitidos + v_ve,
      valortotalvalesaberto = valortotalvalesaberto + v_vv 
  WHERE pkcodmat = :NEW.fkcodmat;

  COMMIT;
END;

这是没有变量的相同触发器:

CREATE OR REPLACE TRIGGER status_chg
AFTER UPDATE OF status ON tbvale
FOR EACH ROW
WHEN (DECODE(NEW.STATUS, OLD.STATUS, 0, 1) = 1)
DECLARE
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  UPDATE tbfuncionario
  SET numvalesdescontados = numvalesdescontados + CASE WHEN :NEW.status = 2 THEN 1 ELSE 0 END
    , numvalesaberto = numvalesaberto + CASE WHEN :NEW.status = 2 THEN -1 ELSE 1 END
    , numvalesemitidos = numvalesemitidos + CASE WHEN :NEW.status = 2 THEN 0 ELSE 1 END
    , valortotalvalesaberto = valortotalvalesaberto + :NEW.valorvale + CASE WHEN :NEW.status = 2 THEN -1 ELSE 1 END
  WHERE pkcodmat = :NEW.fkcodmat;

  COMMIT;
END;

【讨论】:

  • 您好,感谢您的回答。我明白你所做的改变。虽然我在复制代码时犯了一个错误。 :NEW.valorvale 的值应该减去或添加到 valortotalvalesaberto 而不是添加或减去 1(不确定我是否说清楚了)。用 (*) 代替 (+) 很容易纠正,对吗? (valortotalvalesaberto = valortotalvalesaberto + :NEW.valorvale * CASE WHEN :NEW.status = 2 THEN -1 ELSE 1 END)。除此之外,当我尝试您的代码时,我得到“错误:ORA-00906:缺少左括号”。
  • 是的,您的带有因子的解决方案可以正常工作。另一种方法是= valortotalvalesaberto + CASE WHEN :NEW.status = 2 THEN - :NEW.valorvale ELSE :NEW.valorvale END。抱歉缺少括号; WHEN 子句需要它们。我已经更新了我的答案。
【解决方案2】:

您不需要这些导致异常的查询:

SELECT valorvale INTO vv FROM tbvale;
SELECT fkcodmat INTO cod FROM tbvale;

假设您希望更新 tbvale 行中的值,请酌情使用 :old:new 记录,例如

vv := :new.valorvalue;
cod := :new.fkcodmat;

【讨论】:

  • 谢谢!这似乎行得通。我会做一些测试来确定,但到目前为止一切都很好!
猜你喜欢
  • 2012-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-22
  • 1970-01-01
  • 2017-09-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多