根据不同的列生成增量数字
Generating incremental numbers based on a different column
我在 PostgreSQL 的 table 中有一个复合主键(我正在使用 pgAdmin4)
我们将这两个主键称为产品编号和版本。
version代表productno的版本。
因此,如果我创建一个新数据集,则需要检查是否已经存在具有该产品编号的数据集。
- 如果productno还不存在,那么version应该是(version)1
- 如果 productno 存在一次,那么 version 应该是 2
- 如果productno存在两次,那么version应该是3
...等等
所以我们得到类似的东西:
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
我在 PostgreSQL 的 table 中有一个复合主键(我正在使用 pgAdmin4)
我们将这两个主键称为产品编号和版本。
version代表productno的版本。
因此,如果我创建一个新数据集,则需要检查是否已经存在具有该产品编号的数据集。
- 如果productno还不存在,那么version应该是(version)1
- 如果 productno 存在一次,那么 version 应该是 2
- 如果productno存在两次,那么version应该是3
...等等
所以我们得到类似的东西:
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