【问题标题】:How to return a value from a stored procedure (not function)?如何从存储过程(不是函数)返回一个值?
【发布时间】:2020-11-08 21:17:32
【问题描述】:

我有一个插入、更新或删除表行的存储过程。当所有参数都用作输入时,它工作正常。但是,我需要返回最后插入行的 ID。为此,我尝试在INSERT 语句之后使用INOUT 参数和RETURNING 来返回ID。

但是,我不确定如何将返回的 ID 绑定到 INOUT 参数。以下是存储过程的代码:

CREATE OR REPLACE PROCEDURE public.spproductinsertupdatedelete(
_ser integer,
_subcategid integer,
_inrprice numeric,
_usdprice numeric,
_colour integer,
_size integer,
_qty integer,
_prodid integer DEFAULT NULL::integer,
inout _pid integer default null
 )
LANGUAGE 'plpgsql'
AS $BODY$

BEGIN
  if _ser=1 then --- Insert
    INSERT INTO product (prod_subcateg_id,prod_inr_price,prod_usd_price,prod_colour,prod_size,prod_qty)
    VALUES (_subcategID, _inrprice, _usdprice, _colour, _size, _qty)
    RETURNING prod_id;

ELSEIF _ser=2 THEN
    
    UPDATE PRODUCT SET
    prod_subcateg_id = _subcategid,
    prod_inr_price = _inrprice,
    prod_usd_price = _usdprice,
    prod_size = _size,
    prod_colour = _colour,
    prod_qty=_qty
    where prod_id = _prodID;

ELSEIF _ser=3 THEN ---- Delete
    UPDATE PRODUCT SET prod_datetill = now()
    WHERE prod_id = _prodID;
    
end if;

END
$BODY$;

在执行上述存储过程时,我收到此错误:

ERROR:  query has no destination for result data

【问题讨论】:

  • 您可能想使用_pid = INSERT …?
  • 不能使用 OUT,必须使用 INOUT:SP "参数的模式:IN、INOUT 或 VARIADIC。如果省略,则默认为 IN。(OUT 参数为目前不支持过程。请改用 INOUT。)"
  • 如果你想返回一些东西,使用一个函数

标签: postgresql stored-procedures parameter-passing plpgsql


【解决方案1】:

概念证明

PROCEDURE可以返回值,但方式非常有限(从 Postgres 13 开始)。

The manual on CALL:

CALL 执行一个过程。

如果过程有任何输出参数,那么结果行将是 返回,包含这些参数的值。

The manual on CREATE PROCEDURE:

argmode

参数的模式:ININOUTVARIADIC。如果省略,默认为IN。 (过程当前不支持OUT 参数。请改用INOUT。)

所以你使用INOUT 模式是正确的。但是缺少函数体中的赋值。还有一些其他的事情是错误的/次优的。我建议:

CREATE OR REPLACE PROCEDURE public.spproductinsertupdatedelete(
  _ser        int
, _subcategid int
, _inrprice   numeric
, _usdprice   numeric
, _colour     int
, _size       int
, _qty        int
, INOUT _prod_id int DEFAULT NULL
)
  LANGUAGE plpgsql AS
$proc$
BEGIN
   CASE _ser    -- simpler than IF
   WHEN 1 THEN  -- INSERT
      INSERT INTO product
             (prod_subcateg_id, prod_inr_price, prod_usd_price, prod_colour, prod_size, prod_qty)
      VALUES (_subcategid     , _inrprice     , _usdprice     , _colour    , _size    , _qty    )
      RETURNING prod_id
      INTO _prod_id;   -- !!!

   WHEN 2 THEN  -- UPDATE
      UPDATE product
      SET   (prod_subcateg_id, prod_inr_price, prod_usd_price, prod_size, prod_colour, prod_qty)
          = (_subcategid     , _inrprice     , _usdprice     , _size    , _colour    , _qty)
      WHERE  prod_id = _prod_id;

   WHEN 3 THEN  -- soft-DELETE
      UPDATE product
      SET    prod_datetill = now()
      WHERE  prod_id = _prod_id;

   ELSE
      RAISE EXCEPTION 'Unexpected _ser value: %', _ser;
   END CASE;
END
$proc$;

db小提琴here

将此作为概念证明。但我认为问题中没有任何保证首先使用PROCEDURE

你可能想要FUNCTION

FUNCTION 提供了更多返回值的选项,不需要与CALL 单独运行,并且可以集成到更大的查询中。很有可能,这就是您最初想要的,而您只是被普遍的误称“存储过程”所误导。见:

此外,在当前的表单中,如果要更新或软删除一行,则必须提供许多噪声参数。简单的 SQL 命令可能会完成这项工作。或者单独的函数...

经验法则:如果您不需要从内部管理事务,您可能希望使用函数而不是过程。稍后,Postgres 过程可能会扩展为能够返回多个结果集(根据 SQL 标准),但目前还不能(第 13 页)。

见:

【讨论】:

  • 感谢您的解决方案,它有效。我在必须返回结果集的任何地方都使用“函数”,而对于其余的 CRUD 函数,我正在使用过程。这种方法不行吗?我之所以没有做不同的更新和软删除功能是由于表的数量很大。我想有一个“程序”来为每个表进行插入/更新/删除,以便更好地管理。请建议我是否可以在这方面进行改进。
  • @Kartikeya:我添加了一个经验法则和更多细节链接。而且我可能会将函数/过程分成三个单独的用于 INSERT / UPDATE / soft-DELETE - 或者直接使用 SQL 命令。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 2014-10-20
  • 2017-12-22
  • 2016-12-25
  • 2017-07-12
相关资源
最近更新 更多