Oracle PL/SQL触发器不允许从同一个表中选择记录并更新到另一个表中

h4cxqtbf  于 2023-02-07  发布在  Oracle
关注(0)|答案(1)|浏览(202)

我有一个条件,我需要用e1、e2和e3的值更新表x1。假设e1是主键。当X2上的数据被修改时,我需要拉取e2的最大值和e3的最大值来更新表X1。我写了这样的触发器,但是得到了变异表错误。我不想使用PRAGMA自治事务。您的帮助将是appriciated。

create or replace TRIGGER TR_UPDATE_ON_X2
AFTER INSERT OR UPDATE OR DELETE ON X2
FOR EACH ROW
DECLARE
  cnt NUMBER;
  E1 VARCHAR2(20 BYTE);
  E2 NUMBER(3,0);
  E3 VARCHAR2(50 BYTE);
BEGIN
  IF INSERTING OR UPDATING THEN
    SELECT COUNT(1) INTO CNT FROM X1 WHERE E1 = :NEW.E1;
    IF(CNT =0)THEN
       INSERT INTO X1 (E1, E2, E3)  VALUES(:NEW.E1,:NEW.E2,:NEW.E3);
    ELSE  
        SELECT E1,E2,E3 INTO E1,  E2, E3 FROM X1 WHERE E1 = :NEW.E1;
        IF(:NEW.E1 > E1) THEN
            E1 := :NEW.E1;
        END IF;
        IF(:NEW.E2 > E2) THEN
            E2 := :NEW.E2;
        END IF;
        IF(:NEW.E3 > E3) THEN
            E3 := :NEW.E3;
        END IF;
        UPDATE X1 SET E1 = E1, E2 = E2, E3 = E3 WHERE E1 = :NEW.E1;
    END IF;
  ELSIF DELETING THEN
    SELECT COUNT(1) INTO CNT FROM X2 WHERE CONTRACTORSOIDENTIFIER = :NEW.CONTRACTORSOIDENTIFIER;
    IF(CNT != 0)THEN
        SELECT E1,MAX(E2),MAX(E3) INTO E1,  E2, E3 FROM X2 WHERE E1 = :NEW.E1 GROUP BY X2.E1;
        IF(:NEW.E1 > E1) THEN
            E1 := :NEW.E1;
        END IF;
        IF(:NEW.E2 > E2) THEN
            E2 := :NEW.E2;
        END IF;
        IF(:NEW.E3 > E3) THEN
            E3 := :NEW.E3;
        END IF;
        UPDATE X1 SET E1 = E1, E2 = E2, E3 = E3 WHERE E1 = :NEW.E1;
    ELSE
        DELETE FROM X1 WHERE E1 = :NEW.E1;
    END IF;
  END IF;
END;
nle07wnf

nle07wnf1#

因此,x2中每个e1可以有许多行。对于x2中存在的每个e1,x1中存在一行。x1行包含每个e1的e2和e3的最大值。
INSERTING OR UPDATING部分看起来不错,除了变量的名称与列相同。正如William Robertson在请求注解中指出的,SET E1 = E1只是意味着保持值不变。DBMS不会猜测第二个E1应该是另一个E1。通常在变量前面加上v_,以使它们与列名不同。
您的DELETING有一个主要问题,即删除一个x2行后,必须重新计算e1的x1最大值,这意味着我们必须在表发生变化时从x2中进行选择。(由于delete语句可以影响多行,因此AFTER ROW不一定表示AFTER STATEMENT,因此,我们可能正处于变化之中,无法确定地说哪些还在表中,哪些还没有。)
这个问题的解决方案是复合触发器。这种触发器类型结合了before/after row和before/after statement。因此,您可以查看行,查看哪些e1受到影响,然后在处理完所有行后,为所有受影响的e1更新x1。
下面是一个简单的触发器(当然还有优化的空间:-)

CREATE OR REPLACE TRIGGER tr_update_x1_on_x2
FOR INSERT OR UPDATE OR DELETE ON x2
COMPOUND TRIGGER
  v_array SYS.ORA_MINING_NUMBER_NT := SYS.ORA_MINING_NUMBER_NT();

  AFTER EACH ROW IS
  BEGIN
    -- Add e1 to the array for a later insert/update/delete in x1
    v_array.EXTEND;
    v_array(v_array.COUNT) := COALESCE(:new.e1, :old.e1);
  END AFTER EACH ROW;

  AFTER STATEMENT IS
  BEGIN
    -- Delete rows from x1 where the e1 no longer exists in x2
    DELETE FROM x1
    WHERE e1 IN (select * from table(v_array))
    AND e1 NOT IN (select e1 from x2);

    -- Insert/update x1 rows with the new maximums found in x2
    MERGE INTO x1
    USING
    (
      SELECT e1, MAX(e2) AS max_e2, MAX(e3) AS max_e3
      FROM x2
      WHERE e1 IN (select * from table(v_array))
      GROUP BY e1
    ) src ON (src.e1 = x1.e1)
    WHEN MATCHED THEN
      UPDATE SET e2 = src.max_e2, e3 = src.max_e3
    WHEN NOT MATCHED THEN
      INSERT (e1, e2, e3) VALUES (src.e1, src.max_e2, src.max_e3)
  END AFTER STATEMENT;
END tr_update_x1_on_x2;

相关问题