web-dev-qa-db-ja.com

PostgreSQL関数からSETOF行を返す

2つのビュー間の結合を返したい状況があります。それはたくさんの列です。 SQLServerではかなり簡単でした。しかし、PostgreSQLでは結合を行います。 「列定義リストが必要です」というエラーが表示されます。

これを回避する方法はありますか?戻り列の定義を提供したくありません。

CREATE OR REPLACE FUNCTION functionA(username character varying DEFAULT ''::character varying, databaseobject character varying DEFAULT ''::character varying)
  RETURNS SETOF ???? AS
$BODY$
Declare 
SqlString varchar(4000) = '';
BEGIN
IF(UserName = '*') THEN
   Begin
   SqlString  := 'select * from view1 left join ' + databaseobject  + ' as view2 on view1.id = view2.id';
   End;
ELSE
    Begin
    SqlString := 'select * from view3 left join ' + databaseobject  + ' as view2 on view3.id = view2.id';
    End;
END IF; 
execute (SqlString  );
END;
$BODY$
12
user433023

サニタイズ機能

マニュアル PL/pgSQLのすべての基本があります。基本的に、あなたが持っているものは単純化/消毒することができます:

_CREATE OR REPLACE FUNCTION func_a(username text = '', databaseobject text = '')
  RETURNS ???? AS
$func$
BEGIN

RETURN QUERY EXECUTE
format ('SELECT * FROM %s v1 LEFT JOIN %I v2 USING (id)'
       , CASE WHEN username = '*' THEN 'view1' ELSE 'view3' END, databaseobject);

END
$func$  LANGUAGE plpgsql;
_
  • _BEGIN .. END_ in the function body の追加のインスタンスは必要ありませんが、独自のスコープで別のコードブロックを開始する場合を除きます。

  • 標準のSQL連結演算子は_||_です。 _+_は、以前のベンダーの「創造的な」追加です。

  • 二重引用符で囲まない限り、 CaMeL-ケース識別子 を使用しないでください。まったく使用しないことをお勧めします。

  • varchar(4000)も、SQLServerの特定の制限に合わせて調整されています。このデータ型には、Postgresではパフォーマンス上の利点はまったくありません。実際に4000文字の制限が必要な場合にのみ使用してください。ここでは、変数がまったく必要ないことを除いて、 text -を使用します。機能を簡略化しました。

  • format()をまだ使用していない場合は、 こちらのマニュアルを参照してください

戻り値の型

さて、実際の質問です。SQLでは関数が明確に定義された型を返す必要があるため、動的クエリの戻り値の型は少し注意が必要です。返す列定義リストと一致するテーブル、ビュー、または複合型がデータベースにすでにある場合は、次のように使用できます。

_CREATE FUNCTION foo()
  RETURNS SETOF my_view AS
...
_

タイプを作成しながら作成する場合は、匿名レコードを返すことができます。

_CREATE FUNCTION foo()
  RETURNS SETOF record AS
...
_

または、列定義リストに(最も単純な)_RETURNS TABLE_を提供します。

_CREATE FUNCTION foo()
  RETURNS TABLE (col1 int, col2 text, ...) AS
...
_

匿名レコードの欠点:呼び出しごとに列定義リストを提供する必要があるため、これを使用することはほとんどありません。

そもそも_SELECT *_は使いません。列の明確なリストを使用して、それに応じて戻りタイプを返し、宣言します。

_CREATE OR REPLACE FUNCTION func_a(username text = '', databaseobject text = '')
  RETURNS TABLE(col1 int, col2 text, col3 date) AS
$func$
BEGIN

RETURN QUERY EXECUTE
format ('SELECT v1.col1, v1.col2, v2.col3
         FROM %s v1 LEFT JOIN %I v2 USING (id)$f$
       , CASE WHEN username = '*' THEN 'view1' ELSE 'view3' END, databaseobject);

END
$func$;
_

完全に動的なクエリの場合、最初はプレーンSQLクエリを使用します。関数ではありません。

より高度なオプションがありますが、最初に基本を学ぶ必要があるかもしれません。

27