1

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

我们将这两个主键称为 productno 和 version。

version 代表productno 的版本。

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

  • 如果 productno 尚不存在,则 version 应为 (version) 1
  • 如果 productno 存在一次,那么 version 应该是 2
  • 如果 productno 存在两次,那么 version 应该是 3
    ... 等等

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

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

我发现了一个非常相似的问题:复合主键上的自动增量

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

4

3 回答 3

2

您可以将版本号保存在单独的表中(每个“基本 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

于 2019-08-23T08:54:13.407 回答
0

你可以这样做:

-- 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;
于 2019-08-22T13:14:41.770 回答
0

所以 - 我现在开始工作了

因此,如果您希望根据 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,你绝对帮助我朝着正确的方向前进。

于 2019-08-23T08:39:04.527 回答