我是PostgreSQL的新手。我创建了一个函数来将数据插入2个表并更新1个表。
1.获取最新发票号并更新表res_invoice_number。2.插入res_purchase表。3.插入表res_purchase_detail。4.我在Supabase函数中使用了这个函数。5.我调用这个函数的同时在抖发票号好.
我想知道的是,如果客户端同时请求这个函数,它得到的发票号是一样的吗?
谢谢
create or replace function public.rpc_purchase_create(json text)
returns int
language 'plpgsql'
security definer
as $body$
declare json_main jsonb;
declare json_detail jsonb;
declare invoice_number integer;
declare main_id text;
declare client_code text;
declare invoice text;
begin
--assign json
json_main := json::jsonb;
json_detail := (json_main -> 'list_purchase_detail_model') :: jsonb;
--assign sale id
main_id := (json_main ->> 'id') :: text;
client_code := (json_main ->> 'client_id') :: text;
invoice := (json_main ->> 'invoice') :: text;
invoice := split_part(invoice,'/',1);
--increase invoice number
with x as (update res_invoice_number set number = number + 1 where code = 'PO' and client_id=client_code returning number) select x.number into invoice_number from x;
--insert into main
insert into res_purchase (id,client_id,supplier_id,datetime,due_date,invoice,converted,reference,note,list_picture,sub_total,discount_percent,discount_amount,
discount_total,total,vat,vat_total,grand_total,amount_paid,amount_left,active,create_by,create_at,modify_by,modify_at)
values (
main_id,
client_code,
json_main->>'supplier_id',
(json_main->>'datetime')::bigint,
(json_main->>'due_date')::bigint,
concat(invoice,'/' ,trim(to_char(invoice_number,'000000'))),
'',
json_main->>'reference',
json_main->>'note',
(json_main->>'list_picture')::jsonb,
(json_main->>'sub_total')::float4,
(json_main->>'discount_percent')::int2,
(json_main->>'discount_amount')::float4,
(json_main->>'discount_total')::float4,
(json_main->>'total')::float4,
(json_main->>'vat')::float4,
(json_main->>'vat_total')::float4,
(json_main->>'grand_total')::float4,
(json_main->>'amount_paid')::float4,
(json_main->>'amount_left')::float4,
(json_main->>'active')::bool,
json_main->>'create_by',
(json_main->>'create_at')::bigint,
null,null
);
--insert into detail
insert into res_purchase_detail (
purchase_id,
product_id,
product_name,
description,
qty,
cost,
discount,
amount
)
select main_id,
dt.data ->> 'product_id',
dt.data ->> 'product_name',
dt.data ->> 'description',
(dt.data ->> 'qty')::float4,
(dt.data ->> 'cost')::float4,
(dt.data ->> 'discount')::int4,
(dt.data ->> 'amount')::float4
from jsonb_array_elements(json_detail) as dt(data);
return invoice_number;
end;
$body$;
1条答案
按热度按时间rqqzpn5f1#
由于函数在单个
UPDATE
中递增number
,而不是使用单独的SELECT
来获取当前值,后跟UPDATE
,因此无论事务隔离级别如何,并发请求都不会具有相同的发票号。当以PostgreSQL的默认READ COMMITTED
隔离模式运行时,每个请求将具有不同的发票编号。当以更严格的REPEATABLE READ
或SERIALIZABLE
隔离模式运行时,并发请求要么成功,要么发票号不同,要么引发异常;例如:在post函数中,设置发票号的语句
可以简化为
运行以下命令以建立演示环境:
请注意,这些函数定义通过使用
%TYPE
而不是直接声明类型,将代码与列类型更改隔离开来。限定符用于保护函数不受表列和变量之间名称冲突的影响。函数
concurrency_demo_good
使用单个UPDATE
,因此递增number
是一个原子操作。在concurrency_demo_bad
中,invoice_number
的新值在UPDATE
之外建立。开两个会。在两个会话中执行以下操作,以确保两者都具有
READ COMMITTED
隔离级别:接下来,在第一个会话运行中
在运行该查询时,在第二个会话中运行以下命令:
第二个查询要在第一个查询之后才能完成。每个查询的返回值都不同。
在第一个会话中重复相同的查询,但这次在第一个会话的查询完成之前在第二个会话中运行以下操作:
同样,第二个查询要在第一个查询之后才能完成。这一次两个查询将返回相同的值。
对其他隔离级别(
REPEATABLE READ
和SERIALIZABLE
)重复这些步骤会导致第一个会话返回发票编号,第二个会话报告异常。