您可以将版本号保存在单独的表中(每个“基本 PK”值一个)。这比在每次插入时都执行更有效,max() + 1
并且具有对并发事务安全的额外好处。
所以首先我们需要一个记录版本号的表:
create table version_counter
(
product_no integer primary key,
version_nr integer not null
);
然后我们创建一个函数,为给定的 product_no 增加版本并返回新的版本号:
create function next_version(p_product_no int)
returns integer
as
$$
insert into version_counter (product_no, version_nr)
values (p_product_no, 1)
on conflict (product_no)
do update
set version_nr = version_counter.version_nr + 1
returning version_nr;
$$
language sql
volatile;
这里的技巧是insert on conflict
如果传递的 product_no 尚不存在,则增加现有值或插入新行。
对于产品表:
create table product
(
product_no integer not null,
version_nr integer not null,
created_at timestamp default clock_timestamp(),
primary key (product_no, version_nr)
);
然后创建一个触发器:
create function increment_version()
returns trigger
as
$$
begin
new.version_nr := next_version(new.product_no);
return new;
end;
$$
language plpgsql;
create trigger base_table_insert_trigger
before insert on product
for each row
execute procedure increment_version();
这对于并发事务来说是安全的,因为该行将version_counter
针对 product_no 被锁定,直到将行插入产品表的事务被提交 - 这也将提交对 version_counter 表的更改(并释放该行上的锁定)。
如果两个并发事务为 product_no 插入相同的值,则其中一个将等到另一个完成。
如果两个并发事务为 product_no 插入不同的值,则它们可以工作而无需等待另一个。
如果我们然后插入这些行:
insert into product (product_no) values (1);
insert into product (product_no) values (2);
insert into product (product_no) values (3);
insert into product (product_no) values (1);
insert into product (product_no) values (3);
insert into product (product_no) values (2);
产品表如下所示:
select *
from product
order by product_no, version_nr;
product_no | version_nr | created_at
-----------+------------+------------------------
1 | 1 | 2019-08-23 10:50:57.880
1 | 2 | 2019-08-23 10:50:57.947
2 | 1 | 2019-08-23 10:50:57.899
2 | 2 | 2019-08-23 10:50:57.989
3 | 1 | 2019-08-23 10:50:57.926
3 | 2 | 2019-08-23 10:50:57.966
在线示例:https ://rextester.com/CULK95702