触发 RAISE_APPLICATION_ERROR 但执行内部命令

Posted

技术标签:

【中文标题】触发 RAISE_APPLICATION_ERROR 但执行内部命令【英文标题】:Trigger to RAISE_APPLICATION_ERROR but execute inner commands 【发布时间】: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;(我不确定你应该这样做)。我是说您不应该这样做,因为您处于触发器中,并且对每条语句的提交将至少是数据库的成本过程。我认为您应该考虑以另一种方式进行此日志记录。 另外,触发器内的提交可能会弄乱事务的状态。 不能在触发器中提交,除非使用自主事务。 【参考方案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 的答案。

以上是关于触发 RAISE_APPLICATION_ERROR 但执行内部命令的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 进阶 触发器 -- 触发器介绍触发器语法触发器案例

水平触发和边沿触发(IO复用)

大华相机硬触发设置

触发器的应用和用法?

postgreSQL触发器

防止另一个触发器触发触发器[重复]