从upsert方法返回行

时间:2014-06-03 20:02:21

标签: sql postgresql plpgsql upsert postgresql-9.3

我有一个我从文档中修改过的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;

我试图更改返回类型并让函数返回记录,但我似乎无法使其正常工作。

1 个答案:

答案 0 :(得分:2)

使用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?