【问题标题】:Table is mutating on BEFORE DELETE trigger表在 BEFORE DELETE 触发器上发生变异
【发布时间】:2019-05-29 14:10:31
【问题描述】:

基本上,我有这两张桌子:

CREATE TABLE Aposta (
    codMovimento number(10)
        references Movimento(codMovimento),
    Primary key(codMovimento),
    data DATE default sysdate not null,
    valor Number(10,2) not null,
    quotaTotal Number(10,2) not null,
    CodTipoAposta Number(1) not null
        references TipoAposta(CodTipoAposta) 
    CodEstadoAposta Number(1)
        references EstadoAposta(CodEstadoAposta)
);

CREATE TABLE LinhaAposta (
    CodMovimento Number(10)
        references Movimento(CodMovimento),
    CodEvento Number(10)
        references Evento(CodEvento),
    Primary key(CodMovimento,CodEvento),
    quota Number(10,2) not null,
    resultado VARCHAR2(1) not null
    check (resultado in ('1','X','2'))
);

我创建了这个 BEFORE DELETE 触发器来将 quotaTotal 值除以被删除的配额值。它还需要根据 linhaaposta 表的行数更新 codTipoAposta 的值。我已经尝试在 DELETE 之后进行设置,但脚本输出中显示了相同的错误,并且还尝试了 :NEW 而不是 :OLD。

触发器:

create or replace TRIGGER update_Quota_Estado2
BEFORE DELETE ON linhaaposta
FOR EACH ROW
DECLARE
updateCount number(3);
BEGIN
    UPDATE Aposta SET quotaTotal= quotaTotal / :OLD.quota WHERE codMovimento = :OLD.codMovimento;
    SELECT COUNT(*) INTO updateCount FROM linhaaposta WHERE codMovimento = :OLD.codMovimento;
    IF (updateCount = '0') THEN
        UPDATE Aposta SET codTipoAposta = 0 WHERE codMovimento = :OLD.codMovimento;
    END IF;
    IF (updateCount >= '1') THEN
        UPDATE Aposta SET codTipoAposta = 1 WHERE codMovimento = :OLD.codMovimento;
    END IF;
END;

我做了一个类似的 BEFORE INSERT 触发器,它工作得很好,但效果相反,将插入配额的值相乘并将其更新为 Aposta 表上的 quotaTotal。

这是触发器:

CREATE OR REPLACE TRIGGER update_Quota_Estado
BEFORE INSERT ON linhaaposta
FOR EACH ROW
DECLARE
    updateCount number(3);
BEGIN
    UPDATE Aposta SET quotaTotal= quotaTotal * :NEW.quota WHERE codMovimento = :NEW.codMovimento;
    SELECT COUNT(*) INTO updateCount FROM linhaaposta WHERE codMovimento = :NEW.codMovimento;
    IF (updateCount = '0') THEN
        UPDATE Aposta SET codTipoAposta = 0 WHERE codMovimento = :NEW.codMovimento;
    END IF;
    IF (updateCount >= '1') THEN
        UPDATE Aposta SET codTipoAposta = 1 WHERE codMovimento = :NEW.codMovimento;
    END IF;
END;

当第一个触发器被激活时,这是脚本输出:

Error starting at line : 22 in command -
DELETE FROM linhaaposta where codEvento=1
Error report -
ORA-04091: table LUIS.LINHAAPOSTA is mutating, trigger/function may not see it
ORA-06512: at "LUIS.UPDATE_QUOTA_ESTADO2", line 5
ORA-04088: error during execution of trigger 'LUIS.UPDATE_QUOTA_ESTADO2'

从我测试的结果来看,它只是从 SELECT 开始停止工作,在没有它的情况下尝试了它以及关联的 IF 并且它有效。 缺少什么/我该怎么办?

【问题讨论】:

    标签: sql oracle triggers mutating-table


    【解决方案1】:

    您已点击error ORA-04091

    您无法从触发器本身查询导致触发器触发的表。这是 Oracle 设计的,目的是保护事务完整性。

    this other SO post 中所述,一种解决方案是使用autonomous transaction。这可以通过在触发代码的DECLARE 部分添加特定的编译指示来实现;触发器操作发生在单独的数据库事务中,您必须在触发器代码的末尾提交,例如:

    CREATE TRIGGER
        ...
    DECLARE
        PRAGMA AUTONOMOUS_TRANSACTION;
    BEGIN
        ...
        COMMIT;
    END;
    

    【讨论】:

    • 您如何解释触发器 update_Quota_Estado 的工作原理?它们几乎相同,一个在工作,另一个不在。
    • 嘿,再次尝试了 PRAGMA AUTONOMOUS_TRANSACTION 并且它有效,唯一的问题是当 updateCount 值 = 1 时 codTipoAposta 不会改变。
    • @LyZ4RD mm,由于这是一个自治事务,你需要提交它;请参阅我的更新答案。此外,您应该删除数字周围的引号,例如 « updateCount >= 1 » 而不是 « updateCount >= '1' »。
    • 我想通了,因为是删除前,IFs(updateCount)上的数字应该是2和3,而不是0和1。感谢帮助,感谢。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-03
    • 2018-01-02
    相关资源
    最近更新 更多