postgresql - 如何将表行传递给 plpgsql 函数?

标签 postgresql plpgsql

我正在尝试创建一个函数,该函数可以从表中逐行获取行,并为每一行为不同的表生成 1 行或多行。例如,让我们以这个玩具函数为例(注意:在这个例子中,输入和输出具有相同的字段,但在我原来的问题中字段是不同的):

CREATE OR REPLACE FUNCTION toy_function( a integer, b integer )
RETURNS TABLE( x integer, y integer ) AS $$
BEGIN
    FOR i IN 1..2 LOOP
        x := a + b + i;
        y := a * b * i;
        RETURN NEXT;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

它给出了预期的输出:

SELECT * FROM toy_function( 10, 20 );

 x  |  y  
----+-----
 31 | 200
 32 | 400
(2 rows)

但是如果我将表中的行传递给它,就像这样:

WITH data AS (
    SELECT 1 * i AS a, 2 * i AS b
    FROM GENERATE_SERIES( 1, 3, 1 ) as i
)
SELECT
    toy_function( a, b )
FROM
    data;

我得到了一个记录列表,而不是我之前得到的列:

 toy_function 
--------------
 (4,2)
 (5,4)
 (7,8)
 (8,16)
 (10,18)
 (11,36)
(6 rows)

将函数调用包装在 ().* 中返回单独的列,但会大大减慢查询速度(在我原来的问题中,它从 2 秒变成了 6 秒!)。

我也尝试过在子查询中传递输入数据,但失败并出现一个我不太明白的错误:

WITH data AS (
    SELECT 1 * i AS a, 2 * i AS b
    FROM GENERATE_SERIES( 1, 3, 1 ) as i
)
SELECT
    *
FROM
    toy_function(( SELECT * FROM data));

ERROR:  subquery must return only one column
LINE 8: toy_function(( SELECT * FROM data));

有没有办法做到这一点?要将“数据”中的行一一传递给函数,并从函数中获取一个表,其中包含明确的列?

最佳答案

再挖掘几个星期后,我找到了答案:它是 LATERAL JOIN。 在我的示例中,我需要的查询是:

WITH data AS (
    SELECT 1 * i AS a, 2 * i AS b
    FROM GENERATE_SERIES( 1, 3, 1 ) as i
)
SELECT
    f.*
FROM
    data, LATERAL toy_function( a, b ) f;

这给出了我正在寻找的结果:

 x  | y
----+----
  4 |  2
  5 |  4
  7 |  8
  8 | 16
 10 | 18
 11 | 36
(6 rows)

(注意:LATERAL 关键字对于函数是可选的)。

此新连接已添加到 postgresql 9.3,文档 here ,他们明确提到了这种用法:“一个常见的应用程序正在为一个返回集合的函数提供一个参数值”。此外,查询的运行时间现在还可以,不需要原来的 3 倍。

相关帖子(供引用):

How can you expand a "condensed" PostgreSQL row into separate columns?

What is the difference between LATERAL and a subquery in PostgreSQL?

Call a set-returning function with an array argument multiple times

至于将函数调用包装在 ().* 中时运行时间增加的原因,事实证明这是因为解析器中的宏扩展错误,而在执行 LATERAL 连接时不会发生这种情况。有关更多详细信息,请参见此处:

How to avoid multiple function evals with the (func()).* syntax in an SQL query?

关于postgresql - 如何将表行传递给 plpgsql 函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31989496/

相关文章:

PostgreSQL 9.5 : comma separated string using string_agg()

postgresql - 从 plpgsql 中的 FOR 循环切换到基于集合的 SQL 命令

postgresql - postgresql 物化 View 刷新

postgresql - 在 plpgsql 中,如何退出返回记录的函数

sql - 针对一年中的季度或周运行查询

PostgreSQL 基于范围数字字段的唯一索引

sql - 在 Postgres 上如何对具有特定条件的行进行优先级排序?

ruby-on-rails - 在空相关字段上过滤 - Rails

sql - 插入冲突时回滚触发器

sql - 表函数中的 PostgreSQL 参数化 Order By/Limit