为什么没有PRAGMA AUTONOMOUS_TRANSACTION就可以工作

时间:2019-04-24 06:44:56

标签: oracle exception plsql transactions pragma

我对

有误解
  

PRAGMA AUTONOMOUS_TRANSACTION

指令。

据我所知,它用于日志记录或审计过程中,由主程序(自治程序,过程,函数或触发器)独立运行。

我在生成DUP_VAL_ON_INDEX的表上有一个UPDATE。在这种情况下,我调用了一个记录过程,该过程将错误记录到表中。在日志记录过程中,我没有指定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指令,谁能解释我为什么它仍然记录我的错误,并提供一个示例,其中没有记录代码?

谢谢

2 个答案:

答案 0 :(得分:2)

  

谁能解释我为什么它仍然记录我的错误并提供一个   如果未指定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;

输出:

输出将在Update事务中完成Autonomous。在main块中完成的更新将是rolledback,而不是在private proc中被标记为Autonomous的更新

Before Salary of 1001 is 15000 
Before Salary of 1002 is 10000 
After  Salary of 1001 is 15000 
After  Salary of 1002 is 25000 

答案 1 :(得分:0)

PKG_LOGGING.PRC_LOG()有一个commit语句,因此它将提交。

假设您的代码看起来像这样:

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;即可完成。