在PL / pgSQL函数中访问和返回INSERT INTO的结果

时间:2017-03-22 00:18:22

标签: postgresql plpgsql set-returning-functions sql-returning

我目前正在学习很多PostgreSQL,尤其是PLPGSQL,并且正在努力处理函数中的查询结果。 我想在用户表周围创建一个包装器,然后使用结果然后返回它。 在我的情况下,用户和帐户是两个不同的表,我想一次创建它。

我的第一个天真的方法是建立以下内容:

CREATE OR REPLACE FUNCTION schema.create_user_with_login (IN email varchar, IN password varchar, IN firstname varchar DEFAULT NULL, IN surname varchar DEFAULT NULL)
    RETURNS schema.user
    LANGUAGE plpgsql
    VOLATILE 
    RETURNS NULL ON NULL INPUT
    AS 
$$
declare
  created_user schema."user";
begin

  INSERT INTO schema."user" ("firstname", "surname", "email")
    VALUES (firstname, surname, email)
    RETURNING * INTO created_user;

  // [...] create accounts and other data using e.g. created_user.id

  // the query should return the initially created user
  RETURN created_user
end;
$$;

此方法不起作用,因为schema.user具有NOT NULL个字段(具有该约束的域类型)并将为声明的语句抛出异常:

domain schema."USER_ID" does not allow null values

所以也许它可以起作用,但不能在那种有限的环境中使用。

我还尝试使用RETURNS SETOF schema.user并直接使用RETURN QUERY INSERT ....,但这不会返回所有列,而是返回包含所有数据的列。

如何在函数内部提供数据的同时,实现将初始用户对象作为正确的用户行返回的效果?

我正在使用Postgres 9.6。我的版本输出:

PostgreSQL 9.6.1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.2 20140120 (Red Hat 4.8.2-16), 64-bit

1 个答案:

答案 0 :(得分:4)

有几个问题。

1。

  

我还尝试使用RETURNS SETOF schema.user并直接使用RETURN QUERY INSERT ....,但这不会返回所有列,而是   一列包含所有数据。

确定它会返回所有列。您必须 调用 设置返回函数,如下所示:

SELECT * FROM schema.create_user_with_login;

您必须将其声明为RETURNS SETOF foo.users才能与RETURN QUERY合作。

2

将函数声明为STRICTRETURNS NULL ON NULL INPUT的同义词)然后声明NULL参数默认值是无稽之谈:

... firstname varchar DEFAULT NULL, IN surname varchar DEFAULT NULL)

无法将NULL值传递给定义为STRICT的函数,它只返回NULL并且什么都不做。虽然firstnamesurname是可选的,但定义函数strict(或传递空字符串或其他内容)

更多建议

请勿调用您的架构"schema" 完全不要使用reserved word user作为标识符。 尽可能在任何地方使用合法的,小写的,不带引号的标识符。

功能

考虑到所有因素,您的功能可能如下所示:

CREATE OR REPLACE FUNCTION foo.create_user_with_login (_email text
                                                     , _password text
                                                     , _firstname text = NULL
                                                     , _surname text = NULL)

  RETURNS SETOF foo.users AS 
$func$
begin
   RETURN QUERY
   WITH u AS (
      INSERT INTO foo.users (firstname, surname, email)
      VALUES (_firstname, _surname, _email)
      RETURNING *
      )
    , a AS (       -- create account using created_user.id
      INSERT INTO accounts (user_id) 
      SELECT u.user_id FROM u
      )
      --  more chained CTEs with DML statements?
   TABLE u;  -- return the initially created user

end
$func$  LANGUAGE plpgsql;  -- do *not* define it STRICT

是的,这是一个单个 SQL语句,其中包含几个数据修改CTE来完成所有操作。最快,最干净。为方便起见,函数包装器是可选的。相关:

我在函数参数名称前加下划线(_email)以排除命名约定。这完全是可选的,但如果不这样做,则必须知道参数,变量和列名称的范围。

TABLE uSELECT * FROM u的缩写。

将查询结果存储在plpgsql变量中?

三个不同的案例:

  1. 单一值:

  2. 单行

  3. 行集(=表格)

  4. 没有“表变量”,但有其他几个选项: