PL/pgSQL 函数的输入表

Chr*_*ein 1 sql postgresql plpgsql

我想使用带有表和几列的 plpgsql 函数作为输入参数。这个想法是将表格分成块,并对每个部分做一些事情。

我尝试了以下功能:

CREATE OR REPLACE FUNCTION my_func(Integer)
  RETURNS SETOF my_part
AS $$
DECLARE
out my_part;
BEGIN
  FOR i IN 0..$1 LOOP
    FOR out IN
    SELECT * FROM my_func2(SELECT * FROM table1 WHERE id = i)
    LOOP
       RETURN NEXT out;
   END LOOP;
  END LOOP;
  RETURN;
END;
$$
LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

my_func2() 是在每个较小的部分上做一些工作的函数。

CREATE or REPLACE FUNCTION my_func2(table1) 
  RETURNS SETOF my_part2 AS
$$ 
BEGIN
RETURN QUERY
SELECT * FROM table1;
END
$$
LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

如果我运行:

SELECT * FROM my_func(99);
Run Code Online (Sandbox Code Playgroud)

我想我应该收到为每个 ID 处理的前 99 个 ID。但它说以下行有错误:

SELECT * FROM my_func2(select * from table1 where id = i)
Run Code Online (Sandbox Code Playgroud)

错误是:

子查询只允许返回一列

为什么会发生这种情况?有没有简单的方法来解决这个问题?

Erw*_*ter 6

这里有多种误解。在尝试高级魔法之前先学习基础知识。

  • Postgres 没有“表变量”。您一次只能将 1 列或 1 行传递给函数。使用临时表或refcursor(如@Daniel 评论的)来传递整个表。该语法在多个地方无效,因此不清楚您是否真正尝试这样做。
    即使是这样:一次处理一行或重新考虑您的方法并使用基于集合的操作(纯 SQL)而不是传递游标可能会更好。

  • 数据类型my_partmy_part2在您的问题中未定义。可能是题的缺点或者测试用例的问题。

  • 你似乎期望table1的函数体中的表名my_func2()指的是相同(类型!)名称的函数参数,但这至少在两个方面是根本错误的:

    1. 您只能传递。表名是一个标识符,而不是一个值。您需要动态构建查询字符串并EXECUTE在 plpgsql 函数中执行它。尝试搜索,她在 SO 上的许多相关答案。再说一次,这也可能不是您想要的。

    2. table1inCREATE or REPLACE FUNCTION my_func2(table1)类型名称,而不是参数名称。这意味着您的函数需要类型为 的值table1。显然,你有一个同名的表,所以它应该是关联的行类型。

  • RETURN 类型my_func2()必须与您实际返回的内容相匹配。既然你回来了SELECT * FROM table1,那就去做吧RETURNS SETOF table1

  • 它可以只是一个简单的 SQL 函数。

所有这些放在一起:

CREATE or REPLACE FUNCTION my_func2(_row table1) 
  RETURNS SETOF table1 AS
'SELECT ($1).*' LANGUAGE sql;
Run Code Online (Sandbox Code Playgroud)

请注意括号,这对于分解行类型至关重要。根据文档:

此处需要括号以表明它compositecol是列名而不是表名

但还有更多...

  • 不要out用作变量名,它是CREATE FUNCTION语句的关键字。

  • 主查询的语法my_func()更像是伪代码。太多不会加起来。

概念证明

演示表:

CREATE TABLE table1(table1_id serial PRIMARY KEY, txt text);
INSERT INTO table1(txt) VALUES ('a'),('b'),('c'),('d'),('e'),('f'),('g');
Run Code Online (Sandbox Code Playgroud)

辅助功能:

CREATE or REPLACE FUNCTION my_func2(_row table1) 
  RETURNS SETOF table1 AS
'SELECT ($1).*' LANGUAGE sql;
Run Code Online (Sandbox Code Playgroud)

主功能:

CREATE OR REPLACE FUNCTION my_func(int)
  RETURNS SETOF table1 AS
$func$
DECLARE
   rec table1;
BEGIN
  FOR i IN 0..$1 LOOP
     FOR rec IN
        SELECT * FROM table1 WHERE table1_id = i
     LOOP
        RETURN QUERY
        SELECT * FROM my_func2(rec);
     END LOOP;
  END LOOP;
END
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

称呼:

SELECT * FROM my_func(99);
Run Code Online (Sandbox Code Playgroud)

SQL小提琴。

但这实际上只是一个概念证明。还没有什么用。