简体   繁体   中英

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. 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.

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.

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. Rather than IF inside the body, I'd use the triggers WHEN clause instead.
  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. You want the value of one row, so you'd need a WHERE clause. But as you are supposedly interested in the current records value, you can simply use :new.valorvale .
  4. Same for SELECT fkcodmat INTO cod FROM tbvale .
  5. An autonomous trigger needs COMMIT or ROLLBACK at the end.

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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