PostgreSQL - 如何使用先前记录的结果插入新记录
PostgreSQL - How to use the result of previous record to insert a new record
我正在尝试为我的 customer
table 编写一个迁移,其中我的新列 new_number
应该为每个获取增量值(由于业务规则从 10000 开始)公司如下:
id
company_id
new_number
1
1
10000
2
1
10001
3
1
10002
4
2
10000
5
2
10001
我编写了一个查询,它通过使用获取最后插入的数字并将 1 加到新行的函数来实现这一目的。但是太慢了,当 运行 数以千计的行时会导致超时。
create or replace function get_number (id_company uuid)
returns int
language plpgsql
as
$$
declare
new_number integer;
begin
select
c.new_number
into new_number
from
customer c
where
c.company_id = id_company
and c.new_number is not null
order by c.new_number desc
limit 1;
return new_number;
end;
$$;
update
customer c
set
new_number =
case
when get_number(c.company_id) is null then 1 else get_number(c.company_id) + 1
end
where c.new_number is null;
有没有更简单的方法来实现我想要做的事情?
提前致谢!
您可以避免使用 UDF,而是在 ROW_NUMBER
的帮助下使用更新连接:
UPDATE customer AS c1
SET new_number = c2.rn
FROM
(
SELECT id, ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY id) rn
FROM customer
) c2
WHERE c1.id = c2.id;
如果我没理解错你可以尝试ROW_NUMBER
window函数而不是函数。
WITH cte AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY company_id ORDER BY id) rn
FROM customer
)
UPDATE customer t1
SET new_number = cte.rn + 9999
FROM cte
WHERE t1.new_number IS NULL AND t1.id = cte.id
如果这是一个持续的需求,最好为插入创建一个触发器。
create table customer(id serial, company_id int, new_number int);
CREATE OR REPLACE FUNCTION customer_new_number_function()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
IF NEW.new_number is null THEN
select coalesce(max(new_number),0) + 1 into NEW.new_number from customer where company_id = NEW.company_id;
END IF;
RETURN NEW;
END;
$$
CREATE TRIGGER customer_new_number_trigger
BEFORE INSERT ON customer
FOR EACH ROW
EXECUTE PROCEDURE customer_new_number_function();
insert into customer (company_id) values (1),(1),(1),(2),(2);
select * from customer;
id | company_id | new_number
-: | ---------: | ---------:
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 2
db<>fiddle here
如果新列仅用于报告目的(即不作为搜索键),您还可以将 new_column 定义为仅作为视图列。
例子
create view v_company as
select
id, company_id,
9999+row_number() over (partition by company_id order by id) as new_number
from customer;
select * from v_company
order by id;
id|company_id|new_number|
--+----------+----------+
1| 1| 10000|
2| 1| 10001|
3| 1| 10002|
4| 2| 10000|
5| 2| 10001|
这个轻量级解决方案的大优势是您不必在之后解决问题 分配新值的迁移。
由于您的业务逻辑,您不能使用 identity 或 serial 列。
如果您同时插入同一公司的两个客户并且触发器获得相同的当前 max
值,则另一个答案中提议的“触发器”分配解决方案将导致重复。
我将很难保持 new_column
独特且与某些用户定义的序列化没有间隙。
我正在尝试为我的 customer
table 编写一个迁移,其中我的新列 new_number
应该为每个获取增量值(由于业务规则从 10000 开始)公司如下:
id | company_id | new_number |
---|---|---|
1 | 1 | 10000 |
2 | 1 | 10001 |
3 | 1 | 10002 |
4 | 2 | 10000 |
5 | 2 | 10001 |
我编写了一个查询,它通过使用获取最后插入的数字并将 1 加到新行的函数来实现这一目的。但是太慢了,当 运行 数以千计的行时会导致超时。
create or replace function get_number (id_company uuid)
returns int
language plpgsql
as
$$
declare
new_number integer;
begin
select
c.new_number
into new_number
from
customer c
where
c.company_id = id_company
and c.new_number is not null
order by c.new_number desc
limit 1;
return new_number;
end;
$$;
update
customer c
set
new_number =
case
when get_number(c.company_id) is null then 1 else get_number(c.company_id) + 1
end
where c.new_number is null;
有没有更简单的方法来实现我想要做的事情?
提前致谢!
您可以避免使用 UDF,而是在 ROW_NUMBER
的帮助下使用更新连接:
UPDATE customer AS c1
SET new_number = c2.rn
FROM
(
SELECT id, ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY id) rn
FROM customer
) c2
WHERE c1.id = c2.id;
如果我没理解错你可以尝试ROW_NUMBER
window函数而不是函数。
WITH cte AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY company_id ORDER BY id) rn
FROM customer
)
UPDATE customer t1
SET new_number = cte.rn + 9999
FROM cte
WHERE t1.new_number IS NULL AND t1.id = cte.id
如果这是一个持续的需求,最好为插入创建一个触发器。
create table customer(id serial, company_id int, new_number int);
CREATE OR REPLACE FUNCTION customer_new_number_function() RETURNS TRIGGER LANGUAGE PLPGSQL AS $$ BEGIN IF NEW.new_number is null THEN select coalesce(max(new_number),0) + 1 into NEW.new_number from customer where company_id = NEW.company_id; END IF; RETURN NEW; END; $$
CREATE TRIGGER customer_new_number_trigger BEFORE INSERT ON customer FOR EACH ROW EXECUTE PROCEDURE customer_new_number_function();
insert into customer (company_id) values (1),(1),(1),(2),(2);
select * from customer;
id | company_id | new_number -: | ---------: | ---------: 1 | 1 | 1 2 | 1 | 2 3 | 1 | 3 4 | 2 | 1 5 | 2 | 2
db<>fiddle here
如果新列仅用于报告目的(即不作为搜索键),您还可以将 new_column 定义为仅作为视图列。
例子
create view v_company as
select
id, company_id,
9999+row_number() over (partition by company_id order by id) as new_number
from customer;
select * from v_company
order by id;
id|company_id|new_number|
--+----------+----------+
1| 1| 10000|
2| 1| 10001|
3| 1| 10002|
4| 2| 10000|
5| 2| 10001|
这个轻量级解决方案的大优势是您不必在之后解决问题 分配新值的迁移。
由于您的业务逻辑,您不能使用 identity 或 serial 列。
如果您同时插入同一公司的两个客户并且触发器获得相同的当前 max
值,则另一个答案中提议的“触发器”分配解决方案将导致重复。
我将很难保持 new_column
独特且与某些用户定义的序列化没有间隙。