简体   繁体   English

PL / pgSQL函数的输入表

[英]Input table for PL/pgSQL function

I would like to use a plpgsql function with a table and several columns as input parameter. 我想将一个带有表和几列的plpgsql函数用作输入参数。 The idea is to split the table in chunks and do something with each part. 想法是将表拆分为多个块,并对每个部分进行处理。

I tried the following function: 我尝试了以下功能:

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;

my_func2() is the function that does some work on each smaller part. my_func2()是对每个较小部分进行一些工作的函数。

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

If I run: 如果我运行:

SELECT * FROM my_func(99);

I guess I should receive the first 99 IDs processed for each id. 我想我应该收到为每个ID处理的前99个ID。 But it says there is an error for the following line: 但它说以下行有错误:

SELECT * FROM my_func2(select * from table1 where id = i)

The error is: 错误是:

The subquery is only allowed to return one column 子查询只允许返回一列

Why does this happen? 为什么会这样? Is there an easy way to fix this? 有简单的方法可以解决此问题吗?

There are multiple misconceptions here. 这里有多种误解 Study the basics before you try advanced magic. 在尝试高级魔术之前,请先学习基础知识。

  • Postgres does not have "table variables". Postgres没有“表变量”。 You can only pass 1 column or row at a time to a function. 一次只能将1列或1行传递给一个函数。 Use a temporary table or a refcursor (like commented by @Daniel) to pass a whole table. 使用临时表或refcursor (由@Daniel喜欢评论)通过一个完整的表。 The syntax is invalid in multiple places, so it's unclear whether that's what you are actually trying. 该语法在多个地方都是无效的,因此尚不清楚这是否是您真正要尝试的。
    Even if it is: it would probably be better to process one row at a time or rethink your approach and use a set-based operation (plain SQL) instead of passing cursors. 即使是这样:一次处理一行或重新考虑您的方法并使用基于集合的操作(普通SQL)而不是传递游标可能会更好。

  • The data types my_part and my_part2 are undefined in your question. 数据类型my_partmy_part2在您的问题中未定义。 May be a shortcoming of the question or a problem in the test case. 可能是测试案例中的问题或问题的不足。

  • You seem to expect that the table name table1 in the function body of my_func2() refers to the function parameter of the same (type!) name, but this is fundamentally wrong in at least two ways: 您似乎希望my_func2()函数体中的表名table1引用相同(类型!)名称的函数参数,但是从根本上讲,这至少有两种错误:

    1. You can only pass values . 您只能传递 A table name is an identifier , not a value. 表名是标识符 ,而不是值。 You would need to build a query string dynamically and execute it with EXECUTE in a plpgsql function. 您将需要动态构建查询字符串,并使用plpgsql函数中的EXECUTE执行查询字符串。 Try a search, many related answers her on SO. 尝试搜索,因此她获得了许多相关的答案。 Then again, that may also not be what you wanted. 再说一遍,那可能也不是您想要的。

    2. table1 in CREATE or REPLACE FUNCTION my_func2(table1) is a type name , not a parameter name. table1CREATE or REPLACE FUNCTION my_func2(table1)是一种类型的名称 ,而不是参数名称。 It means your function expects a value of the type table1 . 这意味着您的函数需要一个table1类型的值。 Obviously, you have a table of the same name, so it's supposed to be the associated row type. 显然,您有一个同名的表,因此它应该是关联的行类型。

  • The RETURN type of my_func2() must match what you actually return. my_func2()的RETURN类型必须与您实际返回的内容匹配。 Since you are returning SELECT * FROM table1 , make that RETURNS SETOF table1 . 由于您要返回SELECT * FROM table1 ,因此请使RETURNS SETOF table1成为可能。

  • It can just be a simple SQL function. 它可以只是一个简单的SQL函数。

All of that put together: 所有这些放在一起:

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

Note the parentheses, which are essential for decomposing a row type. 请注意括号,这对于分解行类型至关重要。 Per documentation: 每个文档:

The parentheses are required here to show that compositecol is a column name not a table name 此处需要括号以表明compositecol是列名而不是表名

But there is more ... 但是还有更多...

  • Don't use out as variable name, it's a keyword of the CREATE FUNCTION statement. 不要 out 作为变量名,它是CREATE FUNCTION语句的关键字。

  • The syntax of your main query my_func() is more like psudo-code. 您的主要查询my_func()的语法更像是伪代码。 Too much doesn't add up. 太多不会加起来。

Proof of concept 概念证明

Demo table: 演示表:

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

Helper function: 辅助功能:

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

Main function: 主功能:

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;

Call: 呼叫:

SELECT * FROM my_func(99);

SQL Fiddle. SQL提琴。

But it's really just aa proof of concept. 但这实际上只是概念的证明。 Nothing useful, yet. 没什么用。

As the error log is telling you.. you can return only one column in a subquery, so you have to change it to 错误日志告诉您..您只能在子查询中返回一列,因此您必须将其更改为

SELECT my_func2(SELECT Specific_column_you_need FROM hasval WHERE wid = i)

a possible solution can be that you pass to funct2 the primary key of the table your funct2 needs and then you can obtain the whole table by making the SELECT * inside the function 一种可能的解决方案是,将funct2所需的表的主键传递给funct2,然后通过在函数内部进行SELECT *,可以获取整个表

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM