在更新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;
答案 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;