如何在 PostgreSQL 中的函数内返回 SELECT 的结果?
Posted
技术标签:
【中文标题】如何在 PostgreSQL 中的函数内返回 SELECT 的结果?【英文标题】:How to return result of a SELECT inside a function in PostgreSQL? 【发布时间】:2011-12-18 06:11:01 【问题描述】:我在PostgreSQL中有这个函数,但是不知道如何返回查询结果:
CREATE OR REPLACE FUNCTION wordFrequency(maxTokens INTEGER)
RETURNS SETOF RECORD AS
$$
BEGIN
SELECT text, count(*), 100 / maxTokens * count(*)
FROM (
SELECT text
FROM token
WHERE chartype = 'ALPHABETIC'
LIMIT maxTokens
) as tokens
GROUP BY text
ORDER BY count DESC
END
$$
LANGUAGE plpgsql;
但是我不知道如何在 PostgreSQL 函数中返回查询结果。
我发现返回类型应该是SETOF RECORD
吧?但是return命令不对。
这样做的正确方法是什么?
【问题讨论】:
你为什么要计算它们;你的令牌表中有重复的令牌吗?另外:请将表格定义添加到您的问题中。 这是你的全部功能吗?如果函数中没有任何其他语句,则应将其设为LANGUAGE SQL
。
【参考方案1】:
使用RETURN QUERY
:
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
RETURNS TABLE (txt text -- also visible as OUT parameter inside function
, cnt bigint
, ratio bigint)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY
SELECT t.txt
, count(*) AS cnt -- column alias only visible inside
, (count(*) * 100) / _max_tokens -- I added brackets
FROM (
SELECT t.txt
FROM token t
WHERE t.chartype = 'ALPHABETIC'
LIMIT _max_tokens
) t
GROUP BY t.txt
ORDER BY cnt DESC; -- potential ambiguity
END
$func$;
呼叫:
SELECT * FROM word_frequency(123);
显式定义返回类型比返回泛型record
更实用。这样您就不必为每个函数调用提供列定义列表。 RETURNS TABLE
是一种方法。还有其他的。 OUT
参数的数据类型必须与查询返回的内容完全匹配。
仔细选择OUT
参数的名称。它们几乎在函数体中的任何地方都可见。对同名的列进行表限定以避免冲突或意外结果。我对示例中的所有列都这样做了。
但请注意OUT
参数cnt
与同名的列别名之间可能存在命名冲突。在这种特殊情况下 (RETURN QUERY SELECT ...
),Postgres 使用列别名而不是 OUT
参数。不过,这在其他情况下可能会模棱两可。有多种方法可以避免混淆:
-
使用 SELECT 列表中项目的序号位置:
ORDER BY 2 DESC
。例子:
Select first row in each GROUP BY group?
重复表达式ORDER BY count(*)
。
(此处不适用。)设置配置参数plpgsql.variable_conflict
或在函数中使用特殊命令#variable_conflict error | use_variable | use_column
。看:
Naming conflict between function parameter and result of JOIN with USING clause
不要使用“text”或“count”作为列名。在 Postgres 中使用两者都是合法的,但“count”是标准 SQL 中的 reserved word 和基本函数名称,“text”是基本数据类型。可能导致令人困惑的错误。我在示例中使用了txt
和cnt
,您可能需要更明确的名称。
添加了缺失的;
并更正了标头中的语法错误。 (_max_tokens int)
,不是 (int maxTokens)
- type 在 name 之后。
在使用整数除法时,最好先乘后除,以尽量减少舍入误差。或者使用numeric
或浮点类型。见下文。
另类
这是我认为您的查询实际上应该是这样的(计算每个令牌的相对份额):
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
RETURNS TABLE (txt text
, abs_cnt bigint
, relative_share numeric)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY
SELECT t.txt, t.cnt
, round((t.cnt * 100) / (sum(t.cnt) OVER ()), 2) -- AS relative_share
FROM (
SELECT t.txt, count(*) AS cnt
FROM token t
WHERE t.chartype = 'ALPHABETIC'
GROUP BY t.txt
ORDER BY cnt DESC
LIMIT _max_tokens
) t
ORDER BY t.cnt DESC;
END
$func$;
表达式sum(t.cnt) OVER ()
是window function。您可以使用CTE 代替子查询。很漂亮,但是在像这样的简单情况下(主要是在 Postgres 12 之前),子查询通常更便宜。
使用OUT
参数或RETURNS TABLE
(隐式使用OUT
参数)时的最终explicit RETURN
statement is not required(但允许)。
round()
with two parameters 仅适用于 numeric
类型。子查询中的count()
产生一个bigint
结果,而sum()
在这个bigint
上产生一个numeric
结果,因此我们自动处理一个numeric
数字,一切就位。
【讨论】:
非常感谢您的回答和更正。现在工作正常(我只将比率类型更改为数字)。 @RenatoDinhaniConceição 酷!我添加了一个版本,它可能会或可能不会回答您实际上没有问过的其他问题。 ;) 很好,唯一的事情是我认为你需要一个RETURN;
之前的 END;
,至少我这样做了 - 但我正在做一个 UNION,所以我不确定这是否成功不同。
@yekta:我添加了一些关于RETURN
角色的信息。修复了一个不相关的错误并添加了一些改进。
当您不想限制 Return TABLE() 中的内容时,有什么方法可以做到这一点。即返回表(*)?【参考方案2】:
您好,请查看以下链接
https://www.postgresql.org/docs/current/xfunc-sql.html
前:
CREATE FUNCTION sum_n_product_with_tab (x int)
RETURNS TABLE(sum int, product int) AS $$
SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;
【讨论】:
是的,最好尽可能使用“纯 SQL”。您可以使用两个或多个命令(SELECT、INSERTS 等),只有最后一个是返回值。程序“逐步依赖”的解决方法是在 WITH 中使用子句链。例如WITH t1 as (SELECT etc1), t2 as (SELECT etc2 from t1) SELECT result FROM t2;
以上是关于如何在 PostgreSQL 中的函数内返回 SELECT 的结果?的主要内容,如果未能解决你的问题,请参考以下文章
如何忽略 PostgreSQL 窗口函数中的空值?或返回列中的下一个非空值
在 PostgreSQL 中,如何创建一个返回实际表的函数? [复制]