【问题标题】:How to return result of a SELECT inside a function in PostgreSQL?如何在 PostgreSQL 中的函数内返回 SELECT 的结果?
【发布时间】: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

标签: sql postgresql return plpgsql return-type


【解决方案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 参数。不过,这在其他情况下可能会模棱两可。有多种方法可以避免混淆:

  1. 使用 SELECT 列表中项目的序号位置:ORDER BY 2 DESC。例子:
  2. 重复表达式ORDER BY count(*)
  3. (此处不适用。)设置配置参数plpgsql.variable_conflict或在函数中使用特殊命令#variable_conflict error | use_variable | use_column。看:

不要使用“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;
猜你喜欢
  • 2017-09-06
  • 1970-01-01
  • 2016-01-12
  • 2016-06-10
  • 1970-01-01
  • 2020-10-18
  • 2021-12-15
  • 1970-01-01
  • 2010-10-19
相关资源
最近更新 更多