如何将 table 行传递给 plpgsql 函数?
How to pass table rows to a plpgsql function?
我正在尝试创建一个函数,可以从 table 中逐行获取行,并且对于每一行,为不同的 table 生成 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)
但是如果我将 table 中的行传递给它,就像这样:
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)
将函数调用包装在 ().*
returns 单独的列中,但会大大降低查询速度(在我原来的问题中,它从 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));
有办法吗?要将 "data" 中的行一一传递给函数并从函数中得到一个 table,带有显式列?
下面是如何使用 table 的记录作为参数的示例:
CREATE FUNCTION your_function_name( paramater_name table_name ) RETURNS INTEGER
AS
$BODY$
RAISE NOTICE 'You can use value from your table : field_x = % ', paramater_name.field_x;
RETURN 0;
$BODY$
LANGUAGE 'plpgsql';
再挖掘几周后,我找到了答案:它是 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,他们明确提到了这个用法:"A common application is providing an argument value for a set-returning function"。此外,查询的运行时间现在可以了,它不需要 3 倍的时间。
相关帖子(供参考):
How can you expand a "condensed" PostgreSQL row into separate columns?
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?
我正在尝试创建一个函数,可以从 table 中逐行获取行,并且对于每一行,为不同的 table 生成 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)
但是如果我将 table 中的行传递给它,就像这样:
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)
将函数调用包装在 ().*
returns 单独的列中,但会大大降低查询速度(在我原来的问题中,它从 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));
有办法吗?要将 "data" 中的行一一传递给函数并从函数中得到一个 table,带有显式列?
下面是如何使用 table 的记录作为参数的示例:
CREATE FUNCTION your_function_name( paramater_name table_name ) RETURNS INTEGER
AS
$BODY$
RAISE NOTICE 'You can use value from your table : field_x = % ', paramater_name.field_x;
RETURN 0;
$BODY$
LANGUAGE 'plpgsql';
再挖掘几周后,我找到了答案:它是 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,他们明确提到了这个用法:"A common application is providing an argument value for a set-returning function"。此外,查询的运行时间现在可以了,它不需要 3 倍的时间。
相关帖子(供参考):
How can you expand a "condensed" PostgreSQL row into separate columns?
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?