PLSQL函数如何返回UPDATED ... RETURNING查询的结果?

时间:2017-05-19 09:21:50

标签: postgresql plpgsql

我有一个需要的功能:

  1. 调用另一个函数
  2. 然后返回更新语句的结果
  3. 在原子性上没有失败
  4. 该功能如下所示:

    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;
    

    到目前为止,我失败了:

    1. 如果我尝试将UPDATE..RETURNING的输出放在变量(rv)中,然后返回该变量:
      1. 变量声明失败(rv SETOF Balances失败,invalid type name "SETOF Balances"
      2. 然后如何将UPDATE..RETURNING的输出放入rv也是一个很好的问题
    2. 如果我尝试执行RETURN QUERY,则会失败并显示syntax error at or near "UPDATE"
    3. 如果我尝试将update语句包装到select语句(SELECT * FROM (UPDATE ... RETURNING))中,那么编译器会抱怨嵌套更新语句的SET部分(syntax error at or near "SET"
    4. 如果我重写整个事件以迭代bal_get_balances(_acct, _currency))输出(SETOF Balances)并执行RETURN NEXT那么问题1-2再次出现,我该如何输出{ {1}}进入psql变量?
    5. 一般的想法是,我可以从主要软件(Ruby,但不相关)中运行此功能,从事务中作为各种模拟,然后根据主应用程序中某种复杂的业务逻辑决定是否去通过交易,或根据余额的确切状态将其丢弃 - 这是迄今为止我想到的将复杂和非常动态的应用业务逻辑与ACID要求相结合的唯一方式。

3 个答案:

答案 0 :(得分:1)

在循环中使用record类型的变量:

declare
    r record;
begin
    -- ...
    for r in 
        update balances
        set updated_at = now()
        -- ...
        returning *
    loop
        return next r;
    end loop;

答案 1 :(得分:1)

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();

http://rextester.com/OUZM15941

答案 2 :(得分:1)

用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)