复合触发器上的PL / SQL触发器变异表

时间:2017-06-07 22:24:03

标签: sql oracle plsql

在更新oracle上另一个表的表上写一个触发器 触发器抛出变异表错误。 我知道错误即将发生,因为我正在更新我的行级块中引用的表,但我无法找到解决方法

编辑:此代码抛出PL / SQL:数字或值错误:NULL索引表键值但我似乎已经绕过变异表。

create or replace TRIGGER DSPLATE_WELL_VOLUME_V3
FOR UPDATE ON DSPLATE 
COMPOUND TRIGGER


TYPE t_PLATE_ID IS TABLE OF DSPLATE.PLATE_ID%TYPE;
v_PLATE_ID  t_PLATE_ID;
TYPE t_NEW_AMOUNT IS TABLE OF DSPLATE.AMOUNT%TYPE;
v_NEW_AMOUNT  t_NEW_AMOUNT;


BEFORE STATEMENT IS
BEGIN
    v_PLATE_ID := t_PLATE_ID();
    v_NEW_AMOUNT := t_NEW_AMOUNT();
END BEFORE STATEMENT;


BEFORE EACH ROW IS
BEGIN
  IF :NEW.PLATE_TYPE != :OLD.PLATE_TYPE AND :NEW.PLATE_TYPE = 'Assay Plate' 
      AND :NEW.AMOUNT_INITIAL IS NOT NULL AND :OLD.AMOUNT_INITIAL IS NULL THEN
           v_PLATE_ID(v_PLATE_ID.LAST) := :OLD.PLATE_ID;
           v_NEW_AMOUNT(v_NEW_AMOUNT.LAST) := :NEW.AMOUNT_INITIAL;
  END IF;
END BEFORE EACH ROW;

AFTER STATEMENT IS
BEGIN
    FOR p IN 1..v_PLATE_ID.LAST LOOP
                  UPDATE DSPLATE_WELL
            SET AMOUNT = AMOUNT - v_NEW_AMOUNT(p)
              WHERE WELL_ID IN (SELECT DSPLATE_WELL.WELL_ID
              FROM DSPLATE INNER JOIN DSPLATE_WELL ON DSPLATE_WELL.PLATE_ID = DSPLATE.PLATE_ID
              WHERE DSPLATE_WELL.WELL_VALUE IN (SELECT DSPLATE_WELL.WELL_VALUE
              FROM DSPLATE_WELL INNER JOIN DSPLATE ON DSPLATE.PLATE_ID = DSPLATE_WELL.PLATE_ID WHERE DSPLATE.PLATE_ID = v_PLATE_ID(p))
              AND DSPLATE.PLATE_TYPE = 'Cherry Pick Plate' AND DSPLATE.LOCATION_ID = 1420);
        END LOOP;
END AFTER STATEMENT;


END;

我写的原始触发器是有效的,但是我希望它被写为复合触发器,当plate_type设置为'assay plate'时,查询查找表DSPLATE_WELL上的plate_id的所有well_values,well_values对应于表DSPLATE上的许多不同的plate_ids与plate_type'cherry pick'和WELL_VALUES最初来自的特定WELL_ID必须更新其音量。

create or replace TRIGGER INSERT_DSPLATE_WELL_VOLUME 
AFTER UPDATE ON DSPLATE  
FOR EACH ROW
DECLARE
pragma autonomous_transaction;


BEGIN
  IF :NEW.PLATE_TYPE != :OLD.PLATE_TYPE AND :NEW.PLATE_TYPE = 'Assay Plate' 
AND :NEW.AMOUNT_INITIAL IS NOT NULL AND :OLD.AMOUNT_INITIAL IS NULL
  THEN
      UPDATE DSPLATE_WELL
        SET AMOUNT = AMOUNT - :NEW.AMOUNT_INITIAL
          WHERE WELL_ID IN (SELECT DSPLATE_WELL.WELL_ID
          FROM DSPLATE INNER JOIN DSPLATE_WELL ON DSPLATE_WELL.PLATE_ID = DSPLATE.PLATE_ID
          WHERE DSPLATE_WELL.WELL_VALUE IN (SELECT DSPLATE_WELL.WELL_VALUE
          FROM DSPLATE_WELL INNER JOIN DSPLATE ON DSPLATE.PLATE_ID = DSPLATE_WELL.PLATE_ID WHERE DSPLATE.PLATE_ID = :OLD.PLATE_ID)
          AND DSPLATE.PLATE_TYPE = 'Cherry Pick Plate' AND DSPLATE.LOCATION_ID = 1420);
          COMMIT;
  END IF;
END;

3 个答案:

答案 0 :(得分:1)

您的触发器应如下所示:

create or replace TRIGGER DSPLATE_WELL_VOLUME_V2
FOR UPDATE ON DSPLATE 
COMPOUND TRIGGER

TYPE t_WELL_ID IS TABLE OF DSPLATE_WELL.WELL_ID%TYPE;
v_WELL_ID  t_WELL_ID := t_WELL_ID();
TYPE t_AMOUNT IS TABLE OF INT;
v_NEW_AMOUNT t_AMOUNT := t_AMOUNT();



TYPE t_PLATE_ID IS TABLE OF DSPLATE.PLATE_ID%TYPE;
v_PLATE_ID  t_PLATE_ID;


BEFORE STATEMENT IS
BEGIN
    v_PLATE_ID := t_PLATE_ID();
END BEFORE STATEMENT;


BEFORE EACH ROW IS
BEGIN
  IF :NEW.PLATE_TYPE != :OLD.PLATE_TYPE AND :NEW.PLATE_TYPE = 'Assay Plate' 
      AND :NEW.AMOUNT_INITIAL IS NOT NULL AND :OLD.AMOUNT_INITIAL IS NULL THEN
           v_PLATE_ID.EXTEND;
           v_PLATE_ID(v_PLATE_ID.LAST) := :OLD.PLATE_ID;
  END IF;
END BEFORE EACH ROW;

AFTER STATEMENT IS
BEGIN

FOR p IN 1..v_PLATE_ID.LAST LOOP

    SELECT DSPLATE_WELL.WELL_ID, :NEW.AMOUNT
    BULK COLLECT INTO v_WELL_ID, v_NEW_AMOUNT
    FROM DSPLATE INNER JOIN DSPLATE_WELL ON DSPLATE_WELL.PLATE_ID = DSPLATE.PLATE_ID
    WHERE DSPLATE_WELL.WELL_VALUE IN 
          (SELECT DSPLATE_WELL.WELL_VALUE
          FROM DSPLATE_WELL INNER JOIN DSPLATE ON DSPLATE.PLATE_ID = DSPLATE_WELL.PLATE_ID 
          WHERE DSPLATE.PLATE_ID = v_PLATE_ID(p))
       AND DSPLATE.PLATE_TYPE = 'Cherry Pick Plate' 
       AND DSPLATE.LOCATION_ID = 1420;

    FOR i in 1..v_WELL_ID.count() LOOP
      UPDATE DSPLATE_WELL SET AMOUNT = (AMOUNT - v_NEW_AMOUNT(i)) 
      WHERE WELL_ID = v_WELL_ID(i);
    END LOOP;

END LOOP;


END AFTER STATEMENT;

END;

注意,此代码未经过测试,并且在性能方面很可能不是最佳的(我假设第二个循环不是必需的,可以放在单个UPDATE语句中)。但是,你应该知道复合触发器是如何工作的。

答案 1 :(得分:0)

我写了类似的复合触发器,我使用“before statement”来捕获值(我使用SELECT填充带有批量收集的结构)和“在每行之后”进行更新。

答案 2 :(得分:0)

create or replace TRIGGER DSPLATE_WELL_VOLUME_V3
FOR UPDATE ON DSPLATE 
COMPOUND TRIGGER



TYPE t_PLATE_ID IS TABLE OF DSPLATE.PLATE_ID%TYPE INDEX BY PLS_INTEGER;
v_PLATE_ID  t_PLATE_ID;
v_NEW_AMOUNT t_PLATE_ID;

counter number := 1;


BEFORE EACH ROW IS
BEGIN
  IF :NEW.PLATE_TYPE != :OLD.PLATE_TYPE AND :NEW.PLATE_TYPE = 'Assay Plate' 
      AND :NEW.AMOUNT_INITIAL IS NOT NULL AND :OLD.AMOUNT_INITIAL IS NULL THEN
           v_PLATE_ID(counter) := :OLD.PLATE_ID;
           v_NEW_AMOUNT(counter) := :NEW.AMOUNT_INITIAL;
           counter := counter + 1;
  END IF;
END BEFORE EACH ROW;

AFTER STATEMENT IS
BEGIN
    FOR p IN 1..v_PLATE_ID.COUNT LOOP
                  UPDATE DSPLATE_WELL
            SET AMOUNT = AMOUNT - v_NEW_AMOUNT(p)
              WHERE WELL_ID IN (SELECT DSPLATE_WELL.WELL_ID
              FROM DSPLATE INNER JOIN DSPLATE_WELL ON DSPLATE_WELL.PLATE_ID = DSPLATE.PLATE_ID
              WHERE DSPLATE_WELL.WELL_VALUE IN (SELECT DSPLATE_WELL.WELL_VALUE
              FROM DSPLATE_WELL INNER JOIN DSPLATE ON DSPLATE.PLATE_ID = DSPLATE_WELL.PLATE_ID WHERE DSPLATE.PLATE_ID = v_PLATE_ID(p))
              AND DSPLATE.PLATE_TYPE = 'Cherry Pick Plate' AND DSPLATE.LOCATION_ID = 1420 AND DSPLATE_WELL.AMOUNT IS NOT NULL);
        END LOOP;
END AFTER STATEMENT;


END;