如何在PostgreSQL中的函数内返回SELECT的结果?


106

我在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命令不正确。

什么是正确的方法?


你为什么要数他们?令牌表中是否有重复的令牌?另外:请将表格定义添加到您的问题中。
wildplasser 2011年

1
这是您的全部功能吗?如果函数中没有其他任何语句,则应使其成为LANGUAGE SQL
jpmc26 2014年

Answers:


134

用途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) 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$  LANGUAGE plpgsql;

呼叫:

SELECT * FROM word_frequency(123);

说明:

  • 这是很多更实用的明确定义的返回类型不是简单地声明为记录。这样,您不必在每次函数调用时都提供列定义列表。RETURNS TABLE是做到这一点的一种方法。还有其他 OUT参数的数据类型必须与查询返回的内容完全匹配。

  • OUT仔细选择参数名称。它们在功能主体中几乎任何位置都可见。对具有相同名称的列进行表限定,以避免冲突或意外结果。我在示例中对所有列进行了此操作。

    但是请注意,参数和同名列别名之间可能存在命名冲突。在这种特殊情况下(),Postgres会以两种方式对参数使用列别名。但是,在其他情况下,这可能是模棱两可的。有多种避免混淆的方法:OUTcntRETURN QUERY SELECT ...OUT

    1. 使用项目在SELECT列表中的顺序位置:ORDER BY 2 DESC。例:
    2. 重复表达式ORDER BY count(*)
    3. (此处不适用。)在功能中设置配置参数plpgsql.variable_conflict或使用特殊命令#variable_conflict error | use_variable | use_column。看到:
  • 不要使用“文本”或“计数”作为列名。两者在Postgres中都是合法使用的,但“ count”是标准SQL中的保留字,并且是基本函数名,“ text”是基本数据类型。可能导致混乱的错误。我在示例中使用txtcnt

  • ;在标题中添加了一个缺失项并更正了语法错误。(_max_tokens int)(int maxTokens)- 键入的名称

  • 使用整数除法时,最好先相乘然后再相除,以最大程度地减少舍入误差。甚至更好:使用numeric(或浮点类型)。见下文。

另类

认为您的查询实际上应该是这样的(计算每个令牌相对份额):

CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
  RETURNS TABLE (txt            text
               , abs_cnt        bigint
               , relative_share numeric) 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$  LANGUAGE plpgsql;

该表达式sum(t.cnt) OVER ()是一个窗口函数。您可以使用CTE代替子查询-漂亮,但是在像这样的简单情况下,子查询通常更便宜。

最后明确RETURN声明要求有工作的时候(但允许)OUT参数或RETURNS TABLE(使隐式使用的OUT参数)。

round()具有两个参数的参数仅适用于numeric类型。count()子查询产生bigint的结果和sum()在此bigint产生numeric的结果,因此,我们对付numeric自动编号和一切都只是属于地方。


非常感谢您的回答和更正。现在工作正常(我仅将比率类型更改为数字)。
Renato Dinhani 2011年

@RenatoDinhaniConceição酷!我添加了一个版本,该版本可能会也可能不会回答您实际上没有问过的其他问题。;)
Erwin Brandstetter

尼斯,唯一的一点是,我认为你需要一个RETURN;在此之前END;,至少我没有-但我做了这么UNION,我不知道这使得它不同。
yekta 2013年

@yekta:我添加了有关角色的一些信息RETURN。修复了一个不相关的错误,并增加了一些改进。
Erwin Brandstetter

1
当您不想限制Return TABLE()中的内容时,该怎么做?IE返回表(*)?
尼克

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.