web-dev-qa-db-ja.com

PostgreSQLの関数内でSELECTの結果を返す方法は?

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である必要があることがわかりました。しかし、戻りコマンドは正しくありません。

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

83
Renato Dinhani

使用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);

説明:

  • 単純にレコードとして宣言するよりも、戻り値の型を明示的に定義する方がmuchより実用的です。この方法では、すべての関数呼び出しで列定義リストを提供する必要はありません。 RETURNS TABLE はそれを行う1つの方法です。他にもあります。 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を使用します。見る:
  • 列名として「テキスト」または「カウント」を使用しないでください。両方ともPostgresでの使用は合法ですが、「count」は、標準SQLの 予約語 です。 「テキスト」は基本的なデータ型です。混乱を招くエラーにつながる可能性があります。私の例ではtxtcntを使用しています。

  • 欠落している;を追加し、ヘッダーの構文エラーを修正しました。 (_max_tokens int)ではなく(int maxTokens)-nameの後のtype

  • 整数除算を使用する場合、丸め誤差を最小限に抑えるために、最初に乗算し、後で除算することをお勧めします。さらに良い: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 ()ウィンドウ関数 です。あなたはcouldサブクエリの代わりに CTE を使用します-きれいですが、サブクエリはこのような単純なケースでは一般的に安価です。

最後の 明示的なRETURNステートメントはnotが必要です (ただし許可されています)OUTパラメーターまたはRETURNS TABLEOUTパラメーターの暗黙的な使用)を使用する場合.

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

108

こんにちは、以下のリンクを確認してください

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;
0
Moumita Das