简体   繁体   English

Oracle PL / SQL触发器可更新其他表

[英]Oracle PL/SQL trigger that updates a different table

I'm doing this exercise for university and I've been stuck for a week. 我正在大学里做这个练习,已经被困了一个星期。 I need to create a trigger so that when the status column on table "tbvale" is changed a few updates happen on column "tbfuncionario" accordingly. 我需要创建一个触发器,以便在更改表“ tbvale”上的状态列时,相应地在列“ tbfuncionario”上进行一些更新。 I realize my code probably looks cumbersome and even inappropriate perhaps but it's an uni exercise designed to teach specific things. 我意识到我的代码可能看起来很笨拙,甚至不合适,但这是设计用来教授特定事物的统一练习。 That's what I came up with so far: 到目前为止,这是我想出的:

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 is the PK of tbfuncionario and fkcodmat is the FK of tbvale referencing pkcodmat. pkcodmat是tbfuncionario的PK,fkcodmat是tbvale引用pkcodmat的FK。

Currently when I run 目前,当我跑步

UPDATE tbvale SET STATUS = 2 WHERE PKCODVALE = 3;

I get the following error: 我收到以下错误:

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

I don't understand why it would "return more than requested number of rows" as there is only one row where pkcodmat is equal to fkcodmat. 我不明白为什么它会“返回超过请求的行数”,因为只有一行pkcodmat等于fkcodmat。

I appreciate any insights as to why that is happening and how I could fix it. 我感谢任何关于为什么会发生这种情况以及如何解决它的见解。

The trigger look quite good. 触发器看起来还不错。 Here are some points: 以下是一些要点:

  1. IF (:NEW.status <> :OLD.status) doesn't detect changes form and to NULL, in case the column is nullable. IF (:NEW.status <> :OLD.status)IF (:NEW.status <> :OLD.status)可为空的情况下,不会检测到形式更改为NULL。 Rather than IF inside the body, I'd use the triggers WHEN clause instead. 我将使用触发器WHEN子句,而不是在主体内部使用IF
  2. You are using column types for the variables. 您正在使用列类型作为变量。 These however are only supposed to hold integers. 但是,这些只能容纳整数。 No need hence for the columns types. 因此,不需要列类型。
  3. SELECT valorvale INTO vv FROM tbvale , This doesn't work. SELECT valorvale INTO vv FROM tbvale ,这不起作用。 You want the value of one row, so you'd need a WHERE clause. 您需要一行的值,因此需要一个WHERE子句。 But as you are supposedly interested in the current records value, you can simply use :new.valorvale . 但是,由于您可能对当前记录的值感兴趣,因此可以简单地使用:new.valorvale
  4. Same for SELECT fkcodmat INTO cod FROM tbvale . SELECT fkcodmat INTO cod FROM tbvaleSELECT fkcodmat INTO cod FROM tbvale
  5. An autonomous trigger needs COMMIT or ROLLBACK at the end. 自治触发器最后需要COMMITROLLBACK

Here is the trigger corrected: 这是已更正的触发器:

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;

And here is the same trigger without variables: 这是没有变量的相同触发器:

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;

You don't need these queries which are causing the exception: 您不需要引起异常的这些查询:

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

Assuming you want the values from the tbvale row being updated, use the :old or :new record as appropriate eg 假设您希望更新tbvale行中的值,请根据tbvale使用:old:new记录,例如

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM