为啥它在没有 PRAGMA AUTONOMOUS_TRANSACTION 的情况下工作
Posted
技术标签:
【中文标题】为啥它在没有 PRAGMA AUTONOMOUS_TRANSACTION 的情况下工作【英文标题】:why it is working without PRAGMA AUTONOMOUS_TRANSACTION为什么它在没有 PRAGMA AUTONOMOUS_TRANSACTION 的情况下工作 【发布时间】:2019-04-24 06:44:55 【问题描述】:我对
有误解PRAGMA AUTONOMOUS_TRANSACTION
指令。
据我所知,它用于记录或审计程序,由主程序(自治、程序、函数或触发器)独立运行。
我对生成 DUP_VAL_ON_INDEX 的表进行了更新。在这个异常中,我调用了一个日志记录过程,它将错误记录到一个表中。在日志记录过程中,我没有指定 PRAGMA AUTONOMOUS_TRANSACTION 指令,但它仍然会在我的日志记录表中插入。
这是我的代码:
create table TEST_PRAGMA
( COL_1 number primary key
, COL_2 number
);
--
insert into TEST_PRAGMA values (1, 200);
insert into TEST_PRAGMA values (2, 200);
--
create table T_LOG
( msg_num number primary key
, MSG_DATE timestamp(6)
, INFO_MSG varchar2(10)
, LONG_MSG varchar2(100)
);
--
create sequence SEQ_TEST start with 1 increment by 1 nocache nocycle;
包装:
create or replace package pkg_logging as
procedure PRC_LOG ( P_MSG_NUM number
, P_MSG_DATE timestamp
, P_INFO_MSG varchar2
, p_long_msg varcahr2);
end PKG_LOGGING;
--
create or replace package body pkg_logging as
procedure PRC_LOG ( P_MSG_NUM number
, P_MSG_DATE timestamp
, P_INFO_MSG varchar2
, P_LONG_MSG VARCHAR2)
as
begin
insert into T_LOG
( MSG_NUM
, MSG_DATE
, INFO_MSG
, LONG_MSG
)
values
( P_MSG_NUM
, P_MSG_DATE
, P_INFO_MSG
, P_LONG_MSG
);
commit;
EXCEPTION
when OTHERS then
rollback;
RAISE_APPLICATION_ERROR(-20000, 'other error has occured: ' || sqlcode || ' - ' || sqlerrm);
end PRC_LOG;
end PKG_LOGGING;
--
set SERVEROUTPUT on;
begin
update TEST_PRAGMA set COL_1 = 1 where COL_2 = 200;
commit;
EXCEPTION
when DUP_VAL_ON_INDEX then
dbms_output.put_line ('DUP_VAL_ON_INDEX error has occured');
PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'error', 'test de logging');
rollback;
end;
因为我没有指定 PRAGMA 指令,所以我希望即使逻辑正确也不会记录错误。
谁能解释一下为什么它仍然记录我的错误并提供一个示例,如果我没有指定 PRAGMA AUTONOMOUS_TRANSACTION 指令,它不会记录代码?
谢谢,
【问题讨论】:
在您的程序中,您使用 COMMIT 进行 INSERT - 除了要插入的行之外,您还期望什么? 从您的过程中删除EXCEPTION when OTHERS then
部分。用一般的 -20000 错误覆盖特定错误没有任何意义。跳过它,错误信息将几乎相同,并且无论如何都会回滚。
只是添加到 Wernfried 的第一条评论:您不需要 AT pragma 来提交对数据库的更改。您需要它在会话中提交一些更改而不提交其他更改。在您的情况下,更新失败,然后您进行了插入,然后您提交了。所以日志行被保存了。
【参考方案1】:
谁能解释我为什么它仍然记录我的错误并提供一个 如果我没有指定 PRAGMA,它不会记录代码的示例 AUTONOMOUS_TRANSACTION 指令好吗?
错误是Inserted
在Log
表中,因为您将其作为Exception handling
处理。您需要将AUTONOMOUS
事务的行为理解为Independent
一段代码,即使主调用proc/pkg
失败,它也会执行。它没有作为Exception Handling
的一部分处理。如下演示所示,您可以看到标记为AUTONOMOUS
的proc 在BEGIN
块中直接调用,而不是在Exception
块中调用以了解行为。
DECLARE
l_salary NUMBER;
--Private Proc marking as Autonomous transaction
procedure nested_block
as
pragma AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE emp
SET salary=salary+15000
WHERE emp_no=1002;
COMMIT;
END;
--Main Block
BEGIN
SELECT salary
INTO l_salary
FROM emp
WHERE emp_no=1001;
Dbms_output.put_line('Before Salary of 1001 is'||l_salary);
SELECT salary
INTO l_salary
FROM emp WHERE emp_no=1002;
Dbms_output.put_line('Before Salary of 1002 is '|| 1_salary);
UPDATE emp
SET
salary = salary + 5000
WHERE emp_no = 1001;
--Calling Autonomous transaction
nested_block;
--And rolling back previous updates.
ROLLBACK;
SELECT salary INTO
l_salary
FROM emp
WHERE emp_no = 1001;
dbms_output.put_line('After Salary of 1001 is'|| l_salary);
SELECT salary
INTO l_salary
FROM emp
WHERE emp_no = 1002;
dbms_output.put_line('After Salary of 1002 is ' || l_salary);
end;
输出:
输出将在Autonomous
事务中完成Update
。在main
块中完成的更新将是rolledback
,但不是在标记为Autonomous
的private proc
中完成的更新
Before Salary of 1001 is 15000
Before Salary of 1002 is 10000
After Salary of 1001 is 15000
After Salary of 1002 is 25000
【讨论】:
【参考方案2】:PKG_LOGGING.PRC_LOG() 有一个提交语句,所以它会提交。
假设您的代码看起来像这样:
set SERVEROUTPUT on;
begin
insert into TEST_PRAGMA values (3, 300);
PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'info', 'inserted a record');
update TEST_PRAGMA set COL_1 = 1 where COL_2 = 200;
commit;
EXCEPTION
when DUP_VAL_ON_INDEX then
dbms_output.put_line ('DUP_VAL_ON_INDEX error has occured');
PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'error', 'test de logging');
rollback;
end;
您将在 TEST_PRAGMA 中有多少条记录? 三个。因为在我们调用 PKG_LOGGING.PRC_LOG() 时插入已提交,因此异常处理程序中的回滚无效。这就是我们应该在审计和日志记录例程中使用PRAGMA AUTONOMOUS_TRANSACTION
的原因:这样我们就可以成功地保存我们的日志消息,而不会影响更广泛的事务。
所以你应该将PRAGMA AUTONOMOUS_TRANSACTION
添加到PKG_LOGGING.PRC_LOG()。
顺便说一句,我认为您应该小心在日志包中使用这样的错误处理程序:
EXCEPTION
when OTHERS then
rollback;
RAISE_APPLICATION_ERROR(-20000, 'other error has occured: ' || sqlcode || ' - ' || sqlerrm);
end PRC_LOG;
在某些情况下,如果我们无法记录重要信息,我们肯定会想要停止我们的进程。但其他时候,我们希望日志记录能够优雅地失败。例如,如果它不能记录错误,我需要夜间批处理运行异常终止,因为该日志是我知道什么(如果有的话)出了什么问题的唯一方法,最好不要让整个事情运行它来运行不完全,我不知道有些事情失败了。但是,如果我只是在 Test 中编写一些跟踪消息,我可能更喜欢在没有完整跟踪集的情况下结束长时间运行的过程,而不是因为日志记录表空间不足而异常终止。
另外,不需要使用raise_application_error()
。只需在回滚后发出raise;
并完成它。
【讨论】:
以上是关于为啥它在没有 PRAGMA AUTONOMOUS_TRANSACTION 的情况下工作的主要内容,如果未能解决你的问题,请参考以下文章
oracle 触发器 pragma autonomous_transaction
#pragma 标记的意义是啥?为啥我们需要#pragma 标记?