如何在 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”是基本数据类型。可能导致令人困惑的错误。我在示例中使用了txtcnt,您可能需要更明确的名称。

添加了缺失的; 并更正了标头中的语法错误。 (_max_tokens int),不是 (int maxTokens) - typename 之后。

在使用整数除法时,最好先乘后除,以尽量减少舍入误差。或者使用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 函数

PostgreSQL函数如何返回数据集 [转]

在 PostgreSQL 中,如何创建一个返回实际表的函数? [复制]

为 PostgreSQL 函数调用获取“范围内”的两行:交叉连接?

如何在 PostgreSql 函数的返回表中设置标识列?