web-dev-qa-db-ja.com

トランザクションを開いたままにせずに結果セットを保持するにはどうすればよいですか?

次のドキュメントは、関数 here から返されるrefcursorを次のように表示する方法を説明しています。

    CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS '
BEGIN
    OPEN $1 FOR SELECT col FROM test;
    RETURN $1;
END;
' LANGUAGE plpgsql;

BEGIN;
SELECT reffunc('funccursor');
FETCH ALL IN funccursor;
COMMIT;

これでうまくいきます。ただし、画面に結果を保持したい場合は、トランザクションを開いたままにする必要があります。 COMMITを実行すると、結果セットが破棄されます。 FETCHとCOMMITの両方を同時に実行すると、最初の結果セットが破棄されます。

トランザクションをコミットするが結果セットを保持する方法はありますか? PgAdminのバージョンは1.18.1です。

5
A-K

カーソルがSQLレベルで [〜#〜] declare [〜#〜] を使用して定義されている場合、現在のトランザクションをコミットした後もカーソルを存続させるオプションWITH HOLDがあります。ドキュメントの引用:

WITH HOLDは、カーソルを作成したトランザクションが正常にコミットした後も、カーソルを引き続き使用できることを指定します

一方、plpgsql関数によって開かれたrefcursorは、トランザクションの終わりに閉じられます。 plpgsql doc を引用:

すべてのポータルは、トランザクションの終了時に暗黙的に閉じられます。したがって、refcursor値は、トランザクションが終了するまでのみ、オープンカーソルを参照するために使用できます。

「親」トランザクションの外部で使用される可能性があるplpgsql関数にカーソルを作成するには、構文の問題だけです。 plpgsqlバリアントではなく、カーソルのSQL実装が必要です。これには、EXECUTEを使用する必要があります。

例として、これはあなたに似た関数のスケルトンですが、トランザクションよりも長いSQLレベルのカーソルを使用しています。

CREATE FUNCTION dyncursor(name text) RETURNS VOID AS
$$
DECLARE
  query text;
BEGIN
  query='SELECT 1 as col1, 2 as col2'; -- sample query
  EXECUTE 'DECLARE ' || quote_ident(name) || ' CURSOR WITH HOLD FOR ' || query;
END
$$ language plpgsql;

デモ:

 test => begin; 
 test => select dyncursor( 'foo'); 
 dyncursor 
 ----------- 
 
(1行)
 test => commit; 
 test => fooからすべてフェッチ; 
 col1 | col2 
 ------ + ------ 
 1 | 2 
(1行)
 test => close foo; 
 CLOSE CURSOR 
8
Daniel Vérité

pgAdmin は単なるGUIです。この質問にはほとんど関係ありません。たまたま、1つ以上のセッションがSQLエディターウィンドウにバインドされ、ウィンドウが閉じられたときに終了します。

トランザクションを開いたままにする場合は、まだCOMMIT(またはROLLBACK)を使用しないでください。

結果セットを本当に "保持​​"したい場合は、永続的に保持する必要がない場合は、tableに書き込みます。おそらく TEMPORARYまたはUNLOGGED table です。一時テーブルは、デフォルトでセッションとともに存続し、終了します。

SQLとPL/pgSQLは、それらの型システムに対してかなり厳密であり、可能であると思われるいくつかのことは(まだ)実装されていません。しかし、あなたがしようとしていることは、おそらくカーソルなしで解決できます。関数にテーブル名を動的に提供する必要がある場合は、入力パラメーターにチェーンされる polymorphic戻り値の型 を使用します。

_CREATE OR REPLACE FUNCTION f_dynamic_select(_tbl anyelement)
 RETURNS SETOF anyelement AS
$func$
BEGIN

RETURN QUERY EXECUTE
'SELECT * FROM ' || pg_typeof(_tbl);

END
$func$ LANGUAGE plpgsql;
_

コール:

_SELECT * FROM  f_dynamic_select(NULL::my_schema.my_table)
_

pg_typeof() は、文字列の連結中に自動的にregtypeに変換されるときに(必要に応じて)自動的にエスケープされるtext値を返すため、このフォームはSQLインジェクションに対して自動的に安全です。

昨日、ポリモーフィック型を扱う密接に関連する回答を書きました。より多くの説明とリンクがあります:
レコード変数からテーブルに値を挿入

通常、単純で単純なSELECTで十分です。テーブル名を動的に提供する必要があるのはまれな状況です。

_SELECT * FROM my_schema.my_table
_
4