如何解决PLSQL / DELETE TRIGGER错误中的此错误

时间:2018-09-24 23:02:16

标签: oracle plsql database-trigger

我正在尝试在plsql中编写一个代码,该代码可以执行以下操作: 在删除行之前,此触发器将检查该行中是否有子级,如果是,则子级成为其祖父级的子级,然后继续删除该行。

所以,这就是我现在所拥有的:

CREATE TABLE sucursal
(
    codsuc     NUMBER (8) PRIMARY KEY,
    ganancia   NUMBER (8) NOT NULL CHECK (ganancia > 0),
    sucpadre   NUMBER (8) REFERENCES sucursal
);

CREATE OR REPLACE TRIGGER borrar_sucursal
    BEFORE DELETE
    ON sucursal
    FOR EACH ROW
    WHEN (old.sucpadre IS NOT NULL)
BEGIN
    DBMS_OUTPUT.PUT_LINE ('Bueno pues lo intente');
    hijo_de_abuelo (:old.codsuc);
END;
/

CREATE OR REPLACE PROCEDURE hijo_de_abuelo (codigo IN sucursal.codsuc%TYPE)
IS
    gato   sucursal.sucpadre%TYPE;
BEGIN
    SELECT sucpadre
      INTO gato
      FROM sucursal
     WHERE codsuc = codigo;

    FOR gallo IN (SELECT *
                    FROM sucursal
                   WHERE sucpadre = codigo)
    LOOP
        UPDATE sucursal
           SET sucpadre = gato
         WHERE codsuc = gallo.codsuc;
    END LOOP;
END;
/

因此,当我尝试在SQL控制台中删除行时会发生以下情况:

SQL> delete from sucursal where codsuc = 2;
Bueno pues lo intente
delete from sucursal where codsuc = 2
            *
ERROR at line 1:
ORA-04091: table PANCRACI0.SUCURSAL is mutating, trigger/function may not see
it
ORA-06512: at "PANCRACI0.HIJO_DE_ABUELO", line 7
ORA-06512: at "PANCRACI0.BORRAR_SUCURSAL", line 3
ORA-04088: error during execution of trigger 'PANCRACI0.BORRAR_SUCURSAL'

任何帮助都是好人。

1 个答案:

答案 0 :(得分:2)

一个选项是编写一个过程,以调用是否应删除表中的一行,而不是直接发出DELETE语句。在该过程中,子节点首先被重定位,然后节点最终被删除。但是,这可能需要您使用该表来更改应用程序。

或者将表重命名以将其放入“背景”。

ALTER TABLE sucursal
            RENAME TO sucursal_t;

然后使用重命名之前创建的表名称创建一个视图。这样就将视图留在了“前景”中。

CREATE VIEW sucursal
AS
SELECT codsuc,
       ganancia,
       sucpadre
       FROM sucursal_t;

您现在可以在该视图上放置一个INSTEAD OF触发器来拦截DELETE。触发器首先重新定位要删除的节点的子节点,然后在“后台”表中删除该节点。

CREATE OR REPLACE TRIGGER borrar_sucursal
                          INSTEAD OF DELETE
                          ON sucursal
                          FOR EACH ROW
BEGIN
    DBMS_OUTPUT.PUT_LINE('Bueno pues lo intente');

    UPDATE sucursal_t
           SET sucpadre = :old.sucpadre
           WHERE sucpadre = :old.codsuc;

    DELETE FROM sucursal_t
           WHERE codsuc = :old.codsuc;
END;
/

db<>fiddle

这样,您现有的代码仍可以像以前一样使用该表。