我可以在Postgresql 9.3数据库中创建以下函数:
create or replace function upsert_expense(
p_id integer,
p_amount integer,
p_payer_id integer,
p_category category_t,
p_description text)
returns text as
$$
begin
if p_id = 0 then
insert into expenses(amount, payer_id, category, description)
values (p_amount, p_payer_id, p_category, p_description)
returning row_to_json(expenses.*);
end if;
update expenses set
amount=p_amount,
payer_id=p_payer_id,
category=p_category,
description=p_description
where id=p_id
returning row_to_json(expenses.*);
end;
$$
language plpgsql;
当我尝试调用该函数时会出现问题:
select upsert_expense(1,1,1,'groceries','description');
我收到以下错误:
ERROR: query has no destination for result data
我认为这是因为“return”关键字的使用不当,我可能会通过使用“pg_get_serial_sequence”得到相同的结果,但我觉得使用“return”会更优雅。
有没有人有任何想法?或者我在这里咆哮错误的树。
答案 0 :(得分:3)
它可以像这样工作:
CREATE OR REPLACE FUNCTION upsert_expense(
p_id integer
,p_amount integer
,p_payer_id integer
,p_category category_t
,p_description text
,OUT p_expenses json)
-- RETURNS json -- not needed, since we use OUT parameter
AS
$func$
BEGIN
IF p_id = 0 THEN
INSERT INTO expenses(amount, payer_id, category, description)
VALUES (p_amount, p_payer_id, p_category, p_description)
RETURNING row_to_json(expenses.*)
INTO p_expenses;
ELSE
UPDATE expenses
SET amount = p_amount
,payer_id = p_payer_id
,category = p_category
,description = p_description
WHERE id = p_id
RETURNING row_to_json(expenses.*)
INTO p_expenses;
END IF;
-- No RETURN needed since we use OUT parameter
END
$func$ LANGUAGE plpgsql;
SQL INSERT
或UPDATE
语句中的RETURNING
子句不会从函数返回。它们只是从函数中的里面返回一个值。使用INTO
子句告诉plpgsql将其写入的位置。
您可以DECLARE
变量foo
并使用... RETURNING INTO foo
及更高版本RETURN foo;
。但是,由于您只想从函数中返回值,请使用快捷方式并使用OUT
parameter开头。
函数row_to_json()
返回数据类型json
,因此变量(或OUT
参数应为匹配类型!
由于您实际上没有从具有SQL RETURNING
子句的函数返回,因此需要重新调整IF
逻辑,以免每次都运行UPDATE
。
在多用户环境中,插入和更新通常容易出现竞争条件。您可能希望使用数据修改CTE并可能锁定或更多。
这与您的函数大致相同,不同之处在于它根据匹配id
的存在来决定是自动更新还是插入,同时使用id = 0
显式指示您的函数:
WITH values(id, amount, payer_id, category, description) AS (
SELECT 1, 2, 3, 'some_val'::category_t, 'some text' -- your values here
)
, upd AS (
UPDATE expenses e
SET amount = v.amount
,payer_id = v.payer_id
,category = v.category
,description = v.description
FROM values v
WHERE e.id = v.id
RETURNING row_to_json(e.*)
)
, ins AS (
INSERT INTO expenses
(amount, payer_id, category, description)
SELECT amount, payer_id, category, description -- add id?
FROM values
WHERE NOT EXISTS (SELECT 1 FROM upd) -- no row found in update
RETURNING row_to_json(expenses.*)
)
SELECT * FROM upd
UNION ALL
SELECT * FROM ins; -- one row guaranteed
最大限度地缩短比赛条件的时间段。您也可以将其包装到SQL或plpgsql函数中。但还有更多......请考虑以下相关问题: