PLSQL 函数如何 return 更新...返回查询的结果?
How can a PLSQL function return a result of an UPDATED...RETURNING query?
我有一个函数需要:
- 调用另一个函数
- 然后是return更新语句的结果
- 原子性不会失败
函数看起来像这样:
CREATE OR REPLACE FUNCTION bal_update_balances(_acct Accounts, _amount INT, _currency INT)
RETURNS SETOF Balances AS $$
DECLARE
-- rv SETOF Balances;
BEGIN
-- STEP 0: initialize balances (make sure they are present in the DB)
PERFORM bal_initialize_balances(_acct, _currency);
-- STEP 1: update & return balances
RETURN QUERY (
UPDATE Balances
SET amount = amount + _amount,
updated_at = now()
WHERE id IN (SELECT id
FROM bal_get_balances(_acct, _currency))
RETURNING *
);
END;
$$ LANGUAGE plpgsql;
到目前为止,我已经失败了:
- 如果我尝试将
UPDATE..RETURNING
的输出放入一个变量 (rv
),然后 return 该变量:
- 变量声明失败(
rv SETOF Balances
失败 invalid type name "SETOF Balances"
)
- 然后如何把
UPDATE..RETURNING
的输出变成rv
也是个好问题
- 如果我尝试做一个
RETURN QUERY
然后它会失败 syntax error at or near "UPDATE"
- 如果我尝试将更新语句包装到 select 语句(
SELECT * FROM (UPDATE ... RETURNING)
)中,那么编译器会抱怨嵌套更新语句的 SET
部分(syntax error at or near "SET"
)
- 如果我重写整个事情以迭代
bal_get_balances(_acct, _currency))
输出(SETOF Balances
)并执行 RETURN NEXT
那么问题 1-2 再次出现,我该如何放置输出UPDATE..RETURNING
到 psql 变量?
一般的想法是,我可以 运行 来自主要软件(Ruby,但无关紧要)的这个功能,从交易中作为某种模拟,然后取决于某种复杂的主应用程序中的业务逻辑决定是通过交易,还是根据余额的确切状态将其丢弃——这是迄今为止我想到的将复杂且非常动态的应用程序业务逻辑与 ACID 要求结合起来的唯一方法。
在循环中使用 record
类型的变量:
declare
r record;
begin
-- ...
for r in
update balances
set updated_at = now()
-- ...
returning *
loop
return next r;
end loop;
create table t(i serial primary key, x int);
insert into t(x) select random()*10 from generate_series(1,10);
create function f() returns setof t language plpgsql as $$
declare
r t[];
begin
-- How to get result into the variable and return it
with a as (update t set x = x*2 where i > 5 returning *)
select array_agg(a.*) into r from a;
return query select * from unnest(r);
-- How to return result of update (just remove parenthesizes around it)
return query
update t set x = x*2 where i > 5 returning *;
end
$$;
select * from f();
只需用 CTE 包装它,如下所示:
t=# CREATE OR REPLACE FUNCTION s161()
RETURNS SETOF s151 AS $$
DECLARE
BEGIN
RETURN QUERY (
with u as (UPDATE s151
SET t= t||'***'
RETURNING *
)
select * from u
);
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION
Time: 1.109 ms
t=# begin;
BEGIN
Time: 0.117 ms
t=# select * from s161() limit 3;
t
---------------
s141***
events***
tg_rep_que***
(3 rows)
我有一个函数需要:
- 调用另一个函数
- 然后是return更新语句的结果
- 原子性不会失败
函数看起来像这样:
CREATE OR REPLACE FUNCTION bal_update_balances(_acct Accounts, _amount INT, _currency INT)
RETURNS SETOF Balances AS $$
DECLARE
-- rv SETOF Balances;
BEGIN
-- STEP 0: initialize balances (make sure they are present in the DB)
PERFORM bal_initialize_balances(_acct, _currency);
-- STEP 1: update & return balances
RETURN QUERY (
UPDATE Balances
SET amount = amount + _amount,
updated_at = now()
WHERE id IN (SELECT id
FROM bal_get_balances(_acct, _currency))
RETURNING *
);
END;
$$ LANGUAGE plpgsql;
到目前为止,我已经失败了:
- 如果我尝试将
UPDATE..RETURNING
的输出放入一个变量 (rv
),然后 return 该变量:- 变量声明失败(
rv SETOF Balances
失败invalid type name "SETOF Balances"
) - 然后如何把
UPDATE..RETURNING
的输出变成rv
也是个好问题
- 变量声明失败(
- 如果我尝试做一个
RETURN QUERY
然后它会失败syntax error at or near "UPDATE"
- 如果我尝试将更新语句包装到 select 语句(
SELECT * FROM (UPDATE ... RETURNING)
)中,那么编译器会抱怨嵌套更新语句的SET
部分(syntax error at or near "SET"
) - 如果我重写整个事情以迭代
bal_get_balances(_acct, _currency))
输出(SETOF Balances
)并执行RETURN NEXT
那么问题 1-2 再次出现,我该如何放置输出UPDATE..RETURNING
到 psql 变量?
一般的想法是,我可以 运行 来自主要软件(Ruby,但无关紧要)的这个功能,从交易中作为某种模拟,然后取决于某种复杂的主应用程序中的业务逻辑决定是通过交易,还是根据余额的确切状态将其丢弃——这是迄今为止我想到的将复杂且非常动态的应用程序业务逻辑与 ACID 要求结合起来的唯一方法。
在循环中使用 record
类型的变量:
declare
r record;
begin
-- ...
for r in
update balances
set updated_at = now()
-- ...
returning *
loop
return next r;
end loop;
create table t(i serial primary key, x int);
insert into t(x) select random()*10 from generate_series(1,10);
create function f() returns setof t language plpgsql as $$
declare
r t[];
begin
-- How to get result into the variable and return it
with a as (update t set x = x*2 where i > 5 returning *)
select array_agg(a.*) into r from a;
return query select * from unnest(r);
-- How to return result of update (just remove parenthesizes around it)
return query
update t set x = x*2 where i > 5 returning *;
end
$$;
select * from f();
只需用 CTE 包装它,如下所示:
t=# CREATE OR REPLACE FUNCTION s161()
RETURNS SETOF s151 AS $$
DECLARE
BEGIN
RETURN QUERY (
with u as (UPDATE s151
SET t= t||'***'
RETURNING *
)
select * from u
);
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION
Time: 1.109 ms
t=# begin;
BEGIN
Time: 0.117 ms
t=# select * from s161() limit 3;
t
---------------
s141***
events***
tg_rep_que***
(3 rows)