【问题标题】:What is the common roll back logic in Oracle PL/SQLOracle PL/SQL中常见的回滚逻辑是什么
【发布时间】:2018-06-08 15:38:48
【问题描述】:

我是 Oracle PL/SQL 编程的新手,但我试图学习如何在一个事务中进行 PL/SQL 中的所有查询,并在这些查询中出现任何问题时正确回滚,以下是我为达到目的而设计的模式:

create or replace procedure transaction_safe(var in varchar2)
is 
begin
    SAVEPOINT start_point;
    /* a lot of DML queries here....*/
    EXCEPTION
        WHEN OTHERS THEN
            ROLLBACK TO start_point;
end;

在 PL/SQL 中设置上面的 SAVEPOINT / EXCEPTION / ROLLBACK 块是否有意义或根本不需要?

【问题讨论】:

标签: oracle plsql


【解决方案1】:

SAVEPOINT 有什么用?

Oracle 10.2 文档有一个good summary of the purpose of SAVEPOINT

使用ROLLBACK TO 语句,保存点撤消部分事务而不是整个事务。

在执行 INSERTUPDATEDELETE 语句之前标记隐式保存点。如果语句失败,则回滚到隐式保存点。通常,只是回滚失败的 SQL 语句,而不是整个事务;如果语句引发未处理的异常,则宿主环境(例如 SQL*Plus)确定回滚的内容。

你已经写了

在一个事务中进行 PL/SQL 中的所有查询

SAVEPOINT 在这种情况下是无用的 - 正如所说的那样,它用于撤销部分事务。如果每个查询都在一个事务中,并且在开始时强制保存点,那么它根本无法帮助我们。

程序

我认为你缺少一些东西:

  • 您不能保证所有查询都位于该初始事务中。

    使用每个步骤两个步骤执行批处理作业会占用大量临时空间。如果您运行这两个步骤而不在它们之间进行提交,那么您可能会用完临时空间,从而导致您和其他正在运行的进程出现问题。

    我认为我们必须假设您可能有一个多阶段因此多程序的过程

  • 您没有引发异常。这意味着如果您遇到数据错误,您将永远不会知道它。您必须向调用代码提出此异常以决定如何处理它。如果 Oracle 代码的每一层都引发异常,即问题是异常的,则语句将自动回滚。

您的程序至少必须是:

create or replace procedure transaction_safe(var in varchar2) is 
begin
  savepoint start_point;

exception when others then
   rollback to start_point;
   raise; -- the important part
end;

我们最终可能会得到一个大致如下所示的调用构造:

procedure run_process is
begin
   transaction1;
   commit;
   transaction2;
   commit;
exception when ...
   ...
end;

该过程实际上应该是一个调度程序链,但这样放置是为了便于查看。

如何使用?

我能想到三种可能性

1。靠它自己

begin
   transaction1();
   commit;
end;

没有直接的问题,调用代码正在执行提交逻辑,我们在您的过程中添加了一个异常,以便我们收到错误通知。如果我们阅读the documentation for ROLLBACK,它会说:

Oracle 建议您使用 COMMIT 或 ROLLBACK 语句显式结束应用程序中的事务。如果没有显式提交事务,程序异常终止,那么Oracle数据库会回滚最后一个未提交的事务。

您已经声明了COMMITROLLBACK,但这不是很明确,因为您的事务控制位于多个位置。如果我们把它放在同一个地方,它应该在调用代码而不是你的过程中,尽管这样做会使两者在功能上非常相似

procedure run_process is
begin
   transaction1();
   commit;
exception when others then
   rollback;
   raise;
end;

这也使代码更具可重用性。 transaction1() 可以根据需要从多个位置调用剥离事务逻辑。因为如果一切都崩溃了,那么一切都会回滚,我们不担心其他过程的事务控制逻辑很差,因为它们只在调用代码时提交。

2。执行多个“交易”

假设您现在在程序中引发了异常,并且我们没有在“事务”之间进行提交

procedure run_process is
begin
   transaction1();
   transaction2();
   commit;
exception when others then
   rollback;
   raise;
end;

假设transaction1() 失败并回滚。在我们的外部异常处理程序中引发异常。不需要过程中的回滚逻辑。

以防万一,如果您没有在 transaction1() 中引发异常,那么您的数据可能会不一致,因为 transaction2() 将开始运行。

如果您在调度程序上运行 transaction1()transaction2(),那么它们只是 (1) 的两个离散示例。

3。循环执行多个事务

换句话说,您的调用代码如下所示:

procedure run_process() is
begin
   for i in (...) loop
      begin
         transaction1();
      exception when others then
         log();
      end;
   end loop;
end;

在这种情况下,您可能不希望单个故障阻止处理您的批次(如果您这样做是 (1) 的示例)。这是您撤消事务的一部分而不是整个事务的地方,因此使用保存点是有意义的。不过,我仍然会将它们放在调用代码中:

procedure run_process() is
begin
   for i in (...) loop
      begin
         savepoint blah;
         transaction1();
      exception when others then
         log();
         rollback to blah;
      end;
   end loop;
end;

tl;dr

您不应该总是使用保存点,因此它们应该进入调用代码以确保代码的可重用性。

【讨论】:

  • 哇,完美的答案。不幸的是,我只能将其标记为答案一次.. :) 谢谢!
猜你喜欢
  • 1970-01-01
  • 2015-03-04
  • 1970-01-01
  • 2011-06-12
  • 1970-01-01
  • 1970-01-01
  • 2020-03-21
  • 1970-01-01
  • 2015-02-28
相关资源
最近更新 更多