触发 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;
在这里,当PRESIDENT
的payment
即将更改时,我想取消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 但执行内部命令的主要内容,如果未能解决你的问题,请参考以下文章