输出多列的PostgreSQL函数或存储过程?

Posted

技术标签:

【中文标题】输出多列的PostgreSQL函数或存储过程?【英文标题】:PostgreSQL function or stored procedure that outputs multiple columns? 【发布时间】:2011-07-24 16:38:19 【问题描述】:

这是我理想中想要的。想象一下,我有一个表格,其中有 A 行。

我想做:

SELECT A, func(A) FROM table

并且输出有 4 列。

有没有办法做到这一点?我见过关于自定义类型的东西,或者任何能让你得到看起来像这样的结果的东西

A,(B,C,D)

但如果我可以让那个函数返回多列而不需要更多的麻烦,那就太好了。

有什么可以做这样的事情吗?

【问题讨论】:

【参考方案1】:

如果函数func只返回1行3个值,如:

CREATE OR REPLACE FUNCTION func
(
    input_val       integer,
    OUT output_val1 integer,
    OUT output_val2 integer,
    OUT output_val3 integer
)
AS $$
BEGIN
  output_val1 := input_val + 1;
  output_val2 := input_val + 2;
  output_val3 := input_val + 3;
END;
$$ LANGUAGE plpgsql;

然后你执行SELECT a, func(a) FROM table1你会得到:

a       | func
integer | record
========|==========
1       | (2, 3, 4)
2       | (3, 4, 5)
3       | (4, 5, 6)

但是,如果你执行:

SELECT a, (f).output_val1, (f).output_val2, (f).output_val3
FROM (SELECT a, func(a) AS f FROM table1) AS x

你会得到:

a       | output_val1 | output_val2 | output_val3
integer | integer     | integer     | integer
========|=============|=============|=============
1       | 2           | 3           | 4
2       | 3           | 4           | 5
3       | 4           | 5           | 6

或者,使用CTE(公用表表达式),如果您执行:

WITH temp AS (SELECT a, func(a) AS f FROM table1)
SELECT a, (f).output_val1, (f).output_val2, (f).output_val3 FROM temp

您还将获得:

a       | output_val1 | output_val2 | output_val3
integer | integer     | integer     | integer
========|=============|=============|=============
1       | 2           | 3           | 4
2       | 3           | 4           | 5
3       | 4           | 5           | 6

注意:您也可以使用以下查询来获得相同的结果:

SELECT a, (f).*
FROM (SELECT a, func(a) AS f FROM table1) AS x

WITH temp AS (SELECT a, func(a) AS f FROM table1)
SELECT a, (f).* FROM temp

【讨论】:

SELECT a, (func(a)).* FROM table1。 postgres 太疯狂了——我喜欢它。 +1 为你 +100 为 postgres @JackPDouglas:很棒的评论!谢谢!【参考方案2】:

我同意 bambam 的回答,但想指出,根据我的测试,JackPDouglas 更简洁的语法 SELECT a, (func(a)).* FROM table1 实际上会为返回的每一列执行一次函数,而 CTE 表达式只会执行一次函数。因此,如果函数执行时间较长,则首选 CTE 表达式。

【讨论】:

+1 虽然值得指出的是,如果函数是IMMUTABLESTABLE,这两种方式都不是很重要【参考方案3】:

如果函数总是返回 3 列,你可以这样做:

CREATE TYPE sometype AS (b INT, c TEXT, d TEXT);

CREATE OR REPLACE FUNCTION func(a TEXT) RETURNS SETOF sometype AS $$
BEGIN
  RETURN QUERY EXECUTE 'SELECT b, c, d FROM ' || a;
END;
$$ LANGUAGE plpgsql;

SELECT a, (f).b, (f).c, (f).d 
FROM (SELECT a, func(a) AS f FROM table) x;

如果您可以从视图中访问表,也许您可​​以通过某种方式创建视图

CREATE VIEW v AS 
SELECT 'tab1' AS a, b, c, d FROM tab1 WHERE 'tab1' IN (SELECT a FROM table)
UNION
SELECT 'tab2' AS a, b, c, d FROM tab2 WHERE 'tab2' IN (SELECT a FROM table)
UNION
SELECT 'tab3' AS a, b, c, d FROM tab3 WHERE 'tab3' IN (SELECT a FROM table);

那么它只是一个SELECT * FROM v。但这又看起来像 Inheritance 可以使用。

【讨论】:

我遇到的问题是它需要用户进行一些非常笨拙的处理。我知道这是否是我唯一的选择,但我正在努力为用户提供更优雅的东西,而不是更少。理想情况下,我希望他们能够执行 SELECT myfunc();它会输出我指定的完整表格,然后从那里开始,可能带有一些标志和诸如此类的东西。也许 SQL 直接不能做到这一点? 您的用户直接执行SQL?我真的不明白这样做的目的。用户将输入一个表名和一些标志,并为每个表名获取不同数量的列和不同类型的列?那么他们将如何处理这个。他们是否也必须为每个表编写不同的语句?那么用户写SELECT myfunc('tablename')而不是写SELECT * FROM tablename有什么好处。还是您所有的表都有类似的表定义?那么你可能想要继承postgresql.org/docs/9.0/interactive/ddl-inherit.html 也许我评论不正确。我想我的意思是我希望能够将你写的很酷的东西压缩成一行 SELECT a, f(a) FROM table;你会得到4列。所以基本上和你的东西一样,只是压缩成一行。不过你肯定很高兴知道......只是想知道为什么我们不能让它更干净? SELECT a, f(a) FROM table 只定义了两个结果列。所以你永远不会得到 4 列。你也许可以做类似SELECT a,b,c,d FROM f((SELECT a FROM v)) 的事情,其中​​ v 是一个视图 CREATE VIEW v AS SELECT array(SELECT a FROM table) a。但不知道这是否更好,在这种情况下,函数必须接受一个数组,对数组的每个成员执行查询并返回所有输入的值和 3 列 CREATE FUNCTION f(IN a_in TEXT[], OUT a TEXT, OUT b INT, OUT c TEXT, OUT d TEXT) RETURNS SETOF record AS $$ ..【参考方案4】:

我想你会想要返回一个包含多列的单条记录吗?在这种情况下,您可以使用返回类型 RECORD 例如。这将允许您返回一个包含任意多列的匿名变量。您可以在此处找到有关所有不同变量的更多信息:

http://www.postgresql.org/docs/9.0/static/plpgsql-declarations.html

关于返回类型:

http://www.postgresql.org/docs/9.0/static/xfunc-sql.html#XFUNC-OUTPUT-PARAMETERS

如果要返回多列的多条记录,请先检查是否必须为此使用存储过程。可以选择只使用VIEW(并使用 WHERE 子句对其进行查询)。如果这不是一个好的选择,则有可能从 9.0 版的存储过程中返回 TABLE

【讨论】:

我认为返回一张桌子可能是一个不错的选择,但考虑到我的使用它有点尴尬。我的意思是我的目标是能够允许用户执行以下操作:SELECT col_a, col_b, func(col_a) FROM a_table;结果将是一个表:col_a、col_b、func_col_a、func_col_b、func_col_c。我基本上是在尝试将复杂的功能包装到函数中,而不是强迫用户使用一堆非常可怕的语法,如下面的答案所示。这个解决方案也是必要的原因是因为我经常不得不使用限制,所以嵌套的东西只会变得更糟。我想要的可能吗?

以上是关于输出多列的PostgreSQL函数或存储过程?的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL Oracle 兼容性之存储过程

存储过程/存储函数:PostgreSQL 中的 SELECT

PostgreSQL 存储过程/函数

PostgreSQL存储过程

没有函数或存储过程的 Amazon RedShift 中的 Upsert

PostgreSQL存储过程-基于SQL的存储过程