如何将多行传递给PostgreSQL函数?

ma1*_*w28 7 postgresql function variadic-functions

我们如何传递一个(无限量)行数组(即常量表)作为PostgreSQL函数的参数/参数?

这是一个想法:

CREATE TYPE foo AS (
    x bigint,
    y smallint,
    z varchar(64)
);

CREATE OR REPLACE FUNCTION bar(bigint, foo[]) RETURNS TABLE(a bigint, x bigint, y smallint, z varchar(64)) AS
$$
    SELECT $1, x, y, z FROM unnest($2);
$$
LANGUAGE SQL;
Run Code Online (Sandbox Code Playgroud)

下面的函数调用有效,但有没有办法缩短它?

SELECT * FROM bar(1, ARRAY[(1,2,'body1'),(2,1,'body2')]::foo[]);
Run Code Online (Sandbox Code Playgroud)

例如,我们无法删除::foo[]强制转换,但是有没有办法重写事物以便我们可以省略它?

我们应该使用变量论证吗?

Phi*_*hil 5

我的Google搜索使我一直处于领先地位,因此我将发布一个答案,该答案可能与OP的需求不完全匹配,但对看到标题如何将多行传递给PostgreSQL函数的其他人可能会有所帮助

OP的原始请求是针对以下类型的:

CREATE TYPE foo AS (
    x bigint,
    y smallint,
    z varchar(64)
);
Run Code Online (Sandbox Code Playgroud)

如果您像我一样,则可能需要将标准SELECT查询的结果传递给函数。因此,假设我有一个表(而不是类型)创建为:

CREATE TABLE foo AS (
    x bigint,
    y smallint,
    z varchar(64)
);
Run Code Online (Sandbox Code Playgroud)

我想将以下结果传递给函数:

SELECT * from foo WHERE x = 12345;
Run Code Online (Sandbox Code Playgroud)

结果可能为零或很多行。

根据https://www.postgresql.org/docs/9.5/static/rowtypes.html上的postgres文档,创建表还会导致创建具有相同名称的复合类型。这很有用,因为它可以自动处理CREATE TYPE foo原始问题,现在我可以将其作为数组传递给函数。

现在,我可以创建一个函数,该函数接受foo类型的值的数组(简化为着眼于传入的内容以及记录的使用方式,而不是返回的内容):

CREATE OR REPLACE FUNCTION bar(someint bigint, foos foo[]) RETURNS ...
LANGUAGE plpgsql
AS $$
DECLARE
    foo_record record;
begin

-- We are going to loop through each composite type value in the array
-- The elements of the composite value are referenced just like 
-- the columns in the original table row
FOREACH foo_record IN ARRAY foos LOOP
  -- do something, maybe like:
  INSERT INTO new_foo (
    x, y, z
  )
  VALUES (
    foo_record.x,
    foo_record.y,
    foo_record.z
  );

END LOOP;

RETURN...
END;
$$;
Run Code Online (Sandbox Code Playgroud)

bar(bigint, foo[])然后可以使用以下命令非常简单地调用此函数:

SELECT bar(4126521, ARRAY(SELECT * from foo WHERE x = 12345));
Run Code Online (Sandbox Code Playgroud)

它以foo类型数组的形式传入foo表中查询的所有行。然后,如我们所见,该函数对这些行中的每一行执行一些操作。

尽管该示例是人为设计的,也许与OP提出的要求不完全相同,但它符合问题的标题,可能使其他人不必进行更多搜索来查找所需内容。

EDIT命名函数参数使事情变得更容易

  • 失败并出现以下错误:错误:子查询必须仅返回一列第 2 行:SELECT bar(4126521, ARRAY(SELECT * from foo WHERE x = 12345)); ^ SQL 状态:42601 字符:22 (3认同)

Cra*_*ger 2

PostgreSQL 还没有表值变量,所以一切都不会很美好。传递数组效率低下,但对于合理大小的输入有效。

对于更大的输入,通常有效的方法是传递引用器。它很笨拙,但对于较大的数据集很实用,有时与临时表结合使用。

例如

CREATE OR REPLACE FUNCTION bar(i bigint, c refcursor) RETURNS TABLE(a bigint, x bigint, y smallint, z varchar(64)) AS
$$
DECLARE
    cursrow foo;
BEGIN
    LOOP
        FETCH NEXT FROM c INTO cursrow;
        a := i;
        x := cursrow.x;
        y := cursrow.y;
        z := cursrow.z;
        RETURN NEXT;
        IF NOT FOUND THEN
            EXIT;
        END IF;
    END LOOP;
    RETURN;
END;
$$;
Run Code Online (Sandbox Code Playgroud)

用法:

demo=> BEGIN;
BEGIN
demo=> DECLARE "curs1" CURSOR FOR VALUES (1,2,'body1'), (2,1,'body2');
DECLARE CURSOR
craig=> SELECT bar(1, 'curs1');
      bar      
---------------
 (1,1,2,body1)
 (1,2,1,body2)
 (1,,,)
(3 rows)

demo=> COMMIT;
COMMIT
Run Code Online (Sandbox Code Playgroud)

不漂亮。但是,plpgsql 从来都不是。遗憾的是它没有行值左值,因为能够编写类似(x, y, z) := cursrowor 的东西ROW(x, y, z) := cursrow会使其不那么难看。

RETURN NEXT有效,但前提是您返回record未命名的参数或TABLE.

遗憾的是,您不能FETCH ALL在子表达式中使用 SQL(不是 plpgsql),因此您不能编写

RETURN QUERY NEXT i, cursrow.* FROM (FETCH ALL FROM c) AS cursrow;
Run Code Online (Sandbox Code Playgroud)