用于更新其他表的Oracle PL / SQL触发器

时间:2017-10-14 09:57:18

标签: sql oracle plsql triggers

我正在为大学做这个练习,我已经被困了一个星期。我需要创建一个触发器,以便当表“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是引用pkcodmat的tbvale的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。

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

2 个答案:

答案 0 :(得分:2)

触发器看起来相当不错。以下是一些观点:

    如果列可以为空,则
  1. IF (:NEW.status <> :OLD.status)不检测更改形式和NULL。而不是身体内的IF,而是使用触发器WHEN子句。
  2. 您正在使用列类型作为变量。然而,这些只应该保持整数。因此不需要列类型。
  3. SELECT valorvale INTO vv FROM tbvale,这不起作用。您想要一行的值,因此您需要一个WHERE子句。但是,由于您可能对当前记录值感兴趣,因此您只需使用:new.valorvale
  4. 即可
  5. SELECT fkcodmat INTO cod FROM tbvale相同。
  6. 自动触发器最后需要COMMITROLLBACK
  7. 以下是纠正的触发器:

    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;
    

答案 1 :(得分:1)

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

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

假设您希望更新tbvale行的值,请根据需要使用:old:new记录,例如

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