INSERT INTO in PL/pgSQL 函数的访问和 return 结果

Access and return result from INSERT INTO in PL/pgSQL function

我目前正在学习很多 PostgreSQL,尤其是 PLPGSQL 并且正在努力处理函数中的查询结果。 我想围绕用户 table 创建一个包装器,稍后使用结果,然后 return 它。 在我的例子中,用户和帐户是两个不同的 table,我想一次性创建它。

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

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.userNOT NULL 个字段(具有该约束的域类型)并且会为声明的语句抛出异常:

domain schema."USER_ID" does not allow null values

所以也许它可以工作,但在那个受限的环境中不行。

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

如何实现 return 将初始用户对象作为适当的用户行,同时在函数内部提供可用数据的效果?

我正在使用 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 期

I also tried to use RETURNS SETOF schema.user and directly RETURN QUERY INSERT ...., but this does not return all columns, but instead one column with all the data.

确定 return所有列。您必须 调用 set-returning 函数,如下所示:

<b>SELECT * FROM</b> 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 的函数,它只会 return NULL 而什么都不做。虽然 firstnamesurname 是可选的,但 不要 定义严格的函数(或传递空字符串或其他东西)

更多建议

不要调用您的架构 "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
  LANGUAGE plpgsql  AS  -- do *not* define it STRICT
$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$;

是的,这是一个 single SQL 语句和几个数据修改 CTE 来完成这一切。最快最干净。为方便起见,函数包装器是可选的。还不如 LANGUAGE sql。相关:

  • Insert data in 3 tables at a time using Postgres

我在函数参数名称前加上下划线 (_email) 以排除命名约定。这完全是可选的,但如果您不这样做,您已经仔细跟踪了冲突参数、变量和列名的范围。

TABLE uSELECT * FROM u.

的缩写

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

三个不同的案例:

  1. 单个值:

    • Store query result in a variable using in PL/pgSQL
  2. 单排

  3. 行集 (= table)

没有"table variables",还有其他几个选项:

  • PostgreSQL table variable