1

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

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()失败(产生任何类型的异常)时,整个修改集将从调用代码中回滚。

目标是处理所有可能的记录,此时我并不关心是否无法处理某些记录,这些失败的记录无论如何都会记录在日志表中(使用自治事务)。

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

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

4

1 回答 1

2

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

另请参阅Firebird 2.5 语言参考中的保存点和 PSQL

PSQL 中不允许使用事务控制语句,因为这会破坏调用过程的语句的原子性。但是,Firebird 确​​实支持在 PSQL 中引发和处理异常,因此可以有选择地撤消在存储过程和触发器中执行的操作,而不会导致整个过程失败。

在内部,自动保存点用于:

  • 撤消BEGIN...END发生异常的块中的所有操作
  • 撤消由过程或触发器执行的所有操作,或者,对于可选过程,自上次暂停以来执行的所有操作,当执行由于未捕获的错误或异常而提前终止时

每个 PSQL 异常处理块也受自动系统保存点的限制。

注意:BEGIN...END块本身不会创建自动保存点。保存点仅在包含用于处理异常的 WHEN 语句的块中创建。

process_one_record在这种情况下,由于需要撤消单个的影响,而不是整个处理process_waiting_records,因此您需要允许从 中抛出异常process_one_record,但在单个过程调用中捕获它。

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

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
于 2020-02-08T11:00:46.260 回答