【问题标题】:Trigger to RAISE_APPLICATION_ERROR but execute inner commands触发 RAISE_APPLICATION_ERROR 但执行内部命令
【发布时间】:2013-12-02 11:45:15
【问题描述】:

我正在处理以下 PL/SQL trigger

CREATE OR REPLACE TRIGGER trigger_1
BEFORE UPDATE ON worker
FOR EACH ROW
DECLARE
BEGIN
  IF :OLD.type = 'PRESIDENT' THEN
    INSERT INTO trigger_log VALUES (sysdate, 'Nope.', 'No change.');
    RAISE_APPLICATION_ERROR(-20111, 'Can not change!');
  END IF;
END;

在这里,当PRESIDENTpayment 即将更改时,我想取消worker 表上的UPDATE 命令。同时,我希望将此命令记录到名为trigger_log 的表中。问题是,当我 RAISE_APPLICATION_ERROR 更新被取消时,日志记录 (INSERT INTO trigger_log) 也被取消了。我怎样才能RAISE_APPLICATION_ERROR 或抛出EXCEPTION,但仍然要在TRIGGER 内运行所有命令?

【问题讨论】:

  • 试一试,在插入命令后放一个commit;(我不确定你应该这样做)。我是说您不应该这样做,因为您处于触发器中,并且对每条语句的提交将至少是数据库的成本过程。我认为您应该考虑以另一种方式进行此日志记录。
  • 另外,触发器内的提交可能会弄乱事务的状态。
  • 不能在触发器中提交,除非使用自主事务。

标签: oracle exception plsql triggers


【解决方案1】:

在引发错误之前,您必须提交您的INSERT-Statement。

也许想想AUTONOMOUS_TRANSACTION

编辑
正如其他人已经说过的那样,您不应该也不能在触发器内commit(异常自主事务)。所以考虑使用 Tom Thomas 的解决方案或调用 logging-procedure/-package。

【讨论】:

    【解决方案2】:

    您可以从触发器中调用stored procedure。该存储过程应声明为PRAGMA AUTONOMOUS_TRANSACTION。试试这样,

        CREATE OR REPLACE 
        PROCEDURE log_error_p
        AS
             PRAGMA AUTONOMOUS_TRANSACTION;
        BEGIN
             INSERT INTO trigger_log VALUES (SYSDATE, 'Nope.', 'No change.');
             COMMIT;
        END;
    

    --

       CREATE OR REPLACE TRIGGER trigger_1
        BEFORE UPDATE ON worker
        FOR EACH ROW
        DECLARE
        BEGIN
             IF :OLD.TYPE = 'PRESIDENT' THEN
                  log_error_p();
             RAISE_APPLICATION_ERROR(-20111, 'Can not change!');
             END IF;
        END;
        /
    

    【讨论】:

      【解决方案3】:

      首先,永远不要在触发器中使用提交或回滚。这是一个编码标准。至于你的问题,我认为这比 raise_application_error 更好

      CREATE OR REPLACE TRIGGER trigger_1
      BEFORE UPDATE ON worker
      REFERENCING OLD as o AND NEW as n
      FOR EACH ROW
      DECLARE
           InsertException EXCEPTION;
      BEGIN
           IF o.TYPE = 'PRESIDENT' THEN
                RAISE InsertException;
           END IF;
      EXCEPTION
           WHEN InsertException THEN
           INSERT INTO trigger_log VALUES (SYSDATE, 'Nope.', 'No change.');
           DBMS_OUTPUT.PUT_LINE(SQLERRM);
      END;
      /
      

      【讨论】:

      • -1 由于您的异常处理程序不会重新引发异常,因此将允许原始更新而不会出错。
      • 所以即使它是更新前的触发器,我需要手动阻止插入?你能详细说明一下吗?我很想知道我犯了什么错误...
      • 您的触发器不会向调用者引发任何异常,因此它将允许更新继续进行。有关此问题的正确答案,请参阅 @Dba 的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-02
      • 2012-05-17
      • 2013-04-16
      • 1970-01-01
      • 2012-04-12
      • 1970-01-01
      相关资源
      最近更新 更多