【问题标题】:Return row from upsert method从 upsert 方法返回行
【发布时间】:2014-06-03 20:02:20
【问题描述】:

我有一个从文档中修改的 upsert 函数。但是我一直在尝试返回更新或插入的行。我从节点应用程序调用此函数,我需要跟踪哪些记录已更新或插入,尤其是在同步脚本期间。

函数如下:

create or replace function upsert_test(d TEXT, sys INT, val INT, p INT, inter BOOLEAN)
returns void as $$
begin
    update test_table set description=d, code=sys, val_id=val, provider_id=p, connect=inter where code=sys and val_id=val and provider_id=p;
    if found then
        return;
    end if;
    begin
        insert into test_table (description, code, val_id, provider_id, connect) values (d, sys, val, p, inter);
        return;
    exception when unique_violation then
    end;
    return;
end;
$$ language plpgsql;

我试图更改返回类型并让函数返回一条记录,但我似乎无法让它工作。

【问题讨论】:

    标签: sql postgresql plpgsql upsert postgresql-9.3


    【解决方案1】:

    使用RETURNING 子句。可以和RETURN QUERY ...结合使用

    CREATE OR REPLACE FUNCTION upsert_t2(d text, sys int, val int, p int, inter bool)
      RETURNS SETOF test_table AS
    $func$
    BEGIN
       RETURN QUERY
       UPDATE test_table t
       SET    description = d
             ,code        = sys
             ,val_id      = val
             ,provider_id = p
             ,connect     = inter
       WHERE  t.code        = sys
       AND    t.val_id      = val
       AND    t.provider_id = p
       RETURNING t.*;
    
       IF FOUND THEN
          RETURN;
       END IF;
    
       BEGIN
          RETURN QUERY
          INSERT INTO test_table (description, code, val_id, provider_id, connect)
          VALUES (d, sys, val, p, inter)
          RETURNING *;
    
       EXCEPTION WHEN UNIQUE_VIOLATION THEN
       END;
    
       RETURN;
    END
    $func$ LANGUAGE plpgsql;

    呼叫:

    SELECT * FROM upsert_t2(...)
    

    回复评论

    我会尽量避免完全不改变任何东西的更新。另外,我会在循环中查看数据修改 CTE:

    CREATE OR REPLACE FUNCTION upsert_cte(d text, sys int, val int, p int
                                                                  , inter bool)
      RETURNS SETOF test_table AS
    $func$
    BEGIN
    
    LOOP
       BEGIN
    
       RETURN QUERY
       WITH sel AS (
          SELECT t.pk_col          -- primary key column
          FROM   test_table t
          WHERE  t.code        = sys
          AND    t.val_id      = val
          AND    t.provider_id = p
          FOR    SHARE             -- lock
          )
       , ins AS (
          INSERT INTO test_table (description, code, val_id, provider_id, connect)
          SELECT d, sys, val, p, inter
          WHERE  NOT EXISTS (SELECT 1 FROM sel)   -- if not found
          RETURNING *
          )
       , upd AS (
          UPDATE test_table t
          SET    description = d
                ,code        = sys
                ,val_id      = val
                ,provider_id = p
                ,connect     = inter
          FROM   sel
          WHERE  sel.pk_col = t.pk_col            -- if found (possibly mult. rows)
          AND    t.description IS DISTINCT FROM d
                ,t.code        IS DISTINCT FROM sys
                ,t.val_id      IS DISTINCT FROM val
                ,t.provider_id IS DISTINCT FROM p
                ,t.connect     IS DISTINCT FROM inter -- only if anything changes
          RETURNING t.*
          )
       SELECT * FROM ins
       UNION ALL
       SELECT * FROM upd;
    
       RETURN;                                  -- No error occurred, exit loop
    
       EXCEPTION WHEN UNIQUE_VIOLATION THEN     -- inserted in concurrent session
          RAISE NOTICE 'It happened!';       -- hardly ever happens, keep looping
       END;
    END LOOP;
    
    END
    $func$ LANGUAGE plpgsql;
    

    此相关答案中的说明和链接:
    Is SELECT or INSERT in a function prone to race conditions?

    【讨论】:

    • 您好,谢谢您的回复。我更新了我的函数,它现在正在返回查询。我唯一担心的是,如果我正在更新/插入的记录存在并且没有进行更新,我不想返回任何内容。我认为 if found then return 会处理这个问题,但似乎并非如此。我将在同步脚本中使用此功能,并尝试跟踪每晚的更新/插入。
    • @Ptrkcon:我添加了另一个解决方案。
    • 再次感谢。我将不得不稍微调整一下以使其按我期望的方式工作。它正在插入我认为应该更新的新行。我想我必须稍微修改一下 'sel' 以适应我对该函数的期望。
    • 如何修改它以仅在 sys 以外的值更改时更新行?例如,如果我想更新 sys=123 的描述,我不想输入一个全新的行(它现在正在做什么)。
    猜你喜欢
    • 1970-01-01
    • 2015-11-17
    • 2013-07-08
    • 2012-09-20
    • 1970-01-01
    • 2016-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多