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

Mad*_*dis 4 postgresql plpgsql sql-returning set-returning-functions

我目前正在学习很多 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;
$$;
Run Code Online (Sandbox Code Playgroud)

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

domain schema."USER_ID" does not allow null values
Run Code Online (Sandbox Code Playgroud)

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

我也尝试RETURNS SETOF schema.user直接使用and 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
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 10

第 1 期

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

当然它返回所有列。您必须像这样调用设置返回函数:

SELECT * FROM schema.create_user_with_login;
Run Code Online (Sandbox Code Playgroud)

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

第二期

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

... firstname varchar DEFAULT NULL, IN surname varchar DEFAULT NULL)
Run Code Online (Sandbox Code Playgroud)

不能将 NULL 值传递给定义的函数STRICT,它只会返回 NULL 而什么都不做。虽然firstnamesurname是可选的,但不要定义严格的函数(或传递空字符串或其他东西)

更多建议

不要调用你的 schema "schema"
根本不要使用保留字 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$;
Run Code Online (Sandbox Code Playgroud)

是的,这是一个带有多个数据修改 CTE的单个SQL 语句来完成所有操作。最快最干净。为方便起见,函数包装器是可选的。也可能是LANGUAGE sql。有关的:

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

TABLE u是 的缩写SELECT * FROM u

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

三种不同的情况:

  1. 单值:

  2. 单排

  3. 行集(= 表)

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