循环中的 Firebird 2.5 commit/rollback(使用自治事务)

Firebird 2.5 commit/rollback (using autonomous transaction) in loop

我必须在存储过程的循环中处理记录,例如:

create or alter procedure process_waiting_records
as
  declare   v_id                type of column my_table.id;
begin
  for
    select
      t.id
    from
      my_table t
    where
      (t.status = 'WAITING_TO_PROCESS')
    order by
      t.created_at
    into
      :v_id
  do
  begin
    execute procedure process_one_record(:v_id);
  end
end ^

问题是当 process_one_record() 的执行失败(产生任何类型的异常)时,整个修改集将从调用代码回滚。

目标是处理所有可能的记录,如果某些记录无法处理,我目前并不关心,那些失败的记录将记录在日志中 table 无论如何(使用自治事务)。

我正在考虑在带有 when any do (dummy code) 子句的自治事务块中调用 process_one_record() 存储过程。但是,我认为那是行不通的,因为那个失败的事务不会被回滚,而是被提交(参考这个话题:)。

有人可以指出正确的方向如何解决这个问题吗?

您不需要匿名交易。当存储过程抛出异常时,该存储过程的影响将自动撤消。如果存储过程包含 SUSPEND,则只会撤消直到最后一个 SUSPEND 的效果(将其视为保存点)。对于任何其他形式的回滚,需要显式回滚事务。

另请参阅 Firebird 2.5 语言参考中的 Savepoints and PSQL

Transaction control statements are not allowed in PSQL, as that would break the atomicity of the statement that calls the procedure. However, Firebird does support the raising and handling of exceptions in PSQL, so that actions performed in stored procedures and triggers can be selectively undone without the entire procedure failing.

Internally, automatic savepoints are used to:

  • undo all actions in the BEGIN...END block where an exception occurs
  • undo all actions performed by the procedure or trigger or, in for a selectable procedure, all actions performed since the last SUSPEND, when execution terminates prematurely because of an uncaught error or exception

Each PSQL exception handling block is also bounded by automatic system savepoints.

NOTE: A BEGIN...END block does not itself create an automatic savepoint. A savepoint is created only in blocks that contain the WHEN statement for handling exceptions.

在这种情况下,由于需要撤消单个 process_one_record 的效果,而不是 process_waiting_records 中的整个处理,您需要允许 [=] 抛出异常15=],但为单个过程调用捕获它。

简而言之,您需要执行以下操作:

for select
  ...
do
begin
  execute procedure process_one_record(:v_id);
  when any do
  begin
    -- do nothing, last call to `process_one_record` was undone
  end
end