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コマンドは正しくありません。

これを行う正しい方法は何ですか?


なぜあなたはそれらを数えるのですか?トークンTABLEに重複したトークンがありますか?また、質問にテーブル定義を追加してください。
wildplasser、2011年

1
これはあなたの全機能ですか?関数に他のステートメントがない場合は、作成するだけLANGUAGE SQLです。
jpmc26 2014年

回答:


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それを行う1つの方法です。他にもあります。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ステートメントは必要ありませ(ただし許可されます)。OUTRETURNS TABLEOUT

round()2つのパラメーターを使用すると、numeric型に対してのみ機能します。count()サブクエリではbigint結果が生成され、sum()これbigintによりnumeric結果が生成されるため、numeric数値が自動的に処理され、すべてが適切に配置されます。


あなたの答えと訂正に感謝します。現在は正常に動作しています(比率のタイプを数値に変更しただけです)。
Renato Dinhani、2011年

@RenatoDinhaniConceiçãoかっこいい!実際に尋ねていない追加の質問に答えるかどうかわからないバージョンを追加しました。;)
Erwin Brandstetter、2011年

いいですね、唯一のことは、そのRETURN;前にあなたがそれを必要とすると思うことです、END;少なくとも私はそうしました-しかし私はUNIONをしているので、それがそれを変えるかどうかはわかりません。
yekta 2013

@yekta:の役割に関する情報をいくつか追加しましたRETURN。無関係なエラーを修正し、その間にいくつかの改善を加えました。
Erwin Brandstetter 2013

1
Return TABLE()の内容を制限したくない場合にこれを行う方法は何ですか。IE RETURN TABLE(*)?
ニック

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.