一尘不染

PostgreSQL中的ROLLBACK事件触发器

sql

我知道这听起来很奇怪,但是有什么方法可以在表中的ROLLBACK事件上调用触发器?我正在查看postgresql触发器文档,在表上只有CREATE,UPDATE,DELETE和INSERT事件。

我的要求是在事务ROLLBACK上,我的触发器将从表中选择last_id并重置值为= last_id + 1的表序列;简而言之,我想在回滚时保留序列值。

任何想法和反馈将不胜感激的家伙!


阅读 211

收藏
2021-03-17

共1个答案

一尘不染

您不能为此使用序列。您需要 所有 插入都必须经过的单个序列化点-否则不能保证“ gapless”属性。您还需要确保不会从该表中删除任何行。

序列化还意味着,只有一个事务可以将行插入到该表中-所有其他插入都必须等待,直到“上一个”插入已提交或回滚为止。

如何实现这一点的一种模式是拥有一张表,其中存储“序列”号。假设我们需要发票号,出于法律原因,发票号必须是无间隙的。

因此,我们首先创建表以保存“当前值”:

create table slow_sequence 
(
  seq_name        varchar(100) not null primary key,
  current_value   integer not null default 0
);

-- create a "sequence" for invoices
insert into slow_sequence values ('invoice');

现在我们需要一个函数,该函数将生成下一个数字,但要确保没有两个事务可以同时获取下一个数字。

create or replace function next_number(p_seq_name text)
  returns integer
as
$$
  update slow_sequence
     set current_value = current_value + 1
  where seq_name = p_seq_name
  returning current_value;
$$
language sql;

该函数将增加计数器并返回增加的值。由于update该序列的行现在被锁定,因此没有其他事务可以更新该值。如果调用事务回滚,则对序列计数器的更新也将回滚。如果已提交,则新值将保留。

为了确保 每个 事务都使用该功能,应创建一个触发器。

创建有问题的表:

create table invoice 
(
  invoice_number integer not null primary key, 
  customer_id    integer not null,
  due_date       date not null
);

现在创建触发器函数和触发器:

create or replace function f_invoice_trigger()
  returns trigger
as
$$
begin
  -- the number is assigned unconditionally so that this can't 
  -- be prevented by supplying a specific number
  new.invoice_number := next_number('invoice');
  return new;
end;
$$
language plpgsql;

create trigger invoice_trigger
  before insert on invoice
  for each row
  execute procedure f_invoice_trigger();

现在,如果一项交易做到了:

insert into invoice (customer_id, due_date) 
values (42, date '2015-12-01');

新号码已生成。然后, 第二个 事务需要等待,直到第一个插入被提交或回滚为止。


正如我所说:此解决方案不可扩展。一点也不。如果该表中有很多插入,它将大大减慢您的应用程序的速度。但是,您不能同时拥有两者:无间隙序列的可伸缩
正确的实现。

我也很确定上面的代码没有涵盖某些极端情况。因此,您很有可能仍然存在差距。

2021-03-17