根据不同的列生成增量数字

Generating incremental numbers based on a different column

我在 PostgreSQL 的 table 中有一个复合主键(我正在使用 pgAdmin4)

我们将这两个主键称为产品编号和版本。

version代表productno的版本。

因此,如果我创建一个新数据集,则需要检查是否已经存在具有该产品编号的数据集。

所以我们得到类似的东西:

  productno | version 
 -----|----------- 
    1 |         1 
    1 |         2 
    1 |         3 
    2 |         1 
    2 |         2 

我发现了一个非常相似的问题:

但我无法使用此解决方案,因为 PostgreSQL 语法显然有点不同 - 因此尝试了很多函数和触发器,但无法找到正确的方法。

你可以这样做:

-- Check if pk exists 

SELECT pk INTO temp_pk FROM table a WHERE a.pk = v_pk1;

-- If exists, inserts it

IF temp_pk IS NOT NULL THEN
  INSERT INTO table(pk, versionpk) VALUES (v_pk1, temp_pk);      
END IF;

所以 - 我现在开始工作了

因此,如果您希望某个列根据 pg sql 中的另一列进行更新 - 看看这个:

这是我使用的函数:

CREATE FUNCTION public.testfunction()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE NOT LEAKPROOF 
AS $BODY$
DECLARE v_productno INTEGER := NEW.productno; 
BEGIN

IF NOT EXISTS (SELECT * 
            FROM testtable 
            WHERE productno = v_productno)
THEN 
NEW.version := 1;
ELSE
NEW.version := (SELECT MAX(testtable.version)+1
                FROM testtable
                WHERE testtable.productno = v_productno);
END IF;
RETURN NEW;
END;
$BODY$;

这是运行函数的触发器:

CREATE TRIGGER testtrigger
    BEFORE INSERT
    ON public.testtable
    FOR EACH ROW
    EXECUTE PROCEDURE public.testfunction();

谢谢@ChechoCZ,你绝对帮助我找到了正确的方向。

您可以将版本号保存在单独的 table 中(每个 "base PK" 值一个)。这比在每次插入时都执行 max() + 1 更有效,并且还有一个额外的好处,即它对并发事务是安全的。

所以首先我们需要一个 table 来跟踪版本号:

create table version_counter 
(
  product_no integer primary key, 
  version_nr integer not null
);

然后我们创建一个函数,为给定的 product_no 和 returns 增加版本号,新版本号:

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 尚不存在,它会增加现有值或插入新行。

产品table:

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 锁定,直到提交将行插入产品 table 的事务 - 这将提交version_counter table 的更改(并释放该行的锁)。

如果两个并发事务为 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);

产品 table 看起来像这样:

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