動的SQLおよびRETURN タイプ
動的SQLを実行したい 。原則として、これはplpgsqlでは EXECUTEの助けを借りて簡単です。 。 必要ありません カーソル。実際、ほとんどの場合、明示的なカーソルがない方がよいでしょう。
遭遇した問題:まだ定義されていないタイプのレコードを返したい 。関数は、 RETURNSで戻り値の型を宣言する必要があります 句(またはOUT またはINOUT パラメーター)。あなたの場合、 number であるため、匿名のレコードにフォールバックする必要があります。 、名前 およびタイプ 返される列の数は異なります。いいね:
CREATE FUNCTION data_of(integer)
RETURNS SETOF record AS ...
ただし、これは特に有用ではありません。すべての呼び出しで列定義リストを提供する必要があります。いいね:
SELECT * FROM data_of(17)
AS foo (colum_name1 integer
, colum_name2 text
, colum_name3 real);
しかし、事前に列がわからない場合は、これをどのように行うのでしょうか?
jsonのような構造化されていないドキュメントデータ型を使用できます。 、jsonb 、hstore またはxml 。参照:
- データベースにデータテーブルを保存するにはどうすればよいですか?
ただし、この質問の目的のために、個々の、正しく入力され、名前が付けられた列を可能な限り返したいと仮定しましょう。
固定リターンタイプのシンプルなソリューション
列datahora 与えられたようです。データ型はtimestampであると想定します。 また、名前とデータ型が異なる2つの列が常に存在すること。
名前 戻り型では総称名を使用することをやめます。
型 私たちも放棄し、すべてをtextにキャストします すべて以降 データ型はtextにキャストできます 。
CREATE OR REPLACE FUNCTION data_of(_id integer)
RETURNS TABLE (datahora timestamp, col2 text, col3 text)
LANGUAGE plpgsql AS
$func$
DECLARE
_sensors text := 'col1::text, col2::text'; -- cast each col to text
_type text := 'foo';
BEGIN
RETURN QUERY EXECUTE '
SELECT datahora, ' || _sensors || '
FROM ' || quote_ident(_type) || '
WHERE id = $1
ORDER BY datahora'
USING _id;
END
$func$;
変数_sensors および_type 代わりに入力パラメータにすることができます。
RETURNS TABLEに注意してください 条項。
RETURN QUERY EXECUTEの使用に注意してください 。これは、動的クエリから行を返すためのより洗練された方法の1つです。
USINGを作成するために、関数パラメーターに名前を使用します RETURN QUERY EXECUTEの句 混乱が少ない。 $1 SQL文字列では、関数パラメータを参照していませんが、USINGで渡された値を参照しています 句。 (どちらもたまたま$1 この簡単な例では、それぞれのスコープで。)
_sensorsの値の例に注意してください :各列はタイプtextにキャストされます 。
この種のコードは、SQLインジェクションに対して非常に脆弱です。 。 quote_ident()を使用します それから保護するために。変数_sensorsにいくつかの列名をまとめます quote_ident()の使用を防止します (そして、通常は悪い考えです!)。たとえば、quote_ident()を使用して列名を個別に実行するなど、他の方法で問題が発生しないようにします。 代わりは。 VARIADIC パラメータが頭に浮かぶ...
PostgreSQL9.1以降のシンプル化
バージョン9.1以降では、format()を使用できます。 さらに単純化するには:
RETURN QUERY EXECUTE format('
SELECT datahora, %s -- identifier passed as unescaped string
FROM %I -- assuming the name is provided by user
WHERE id = $1
ORDER BY datahora'
,_sensors, _type)
USING _id;
繰り返しになりますが、個々の列名は適切にエスケープでき、クリーンな方法になります。
同じタイプを共有する可変数の列
質問が更新された後、返品タイプは次のようになります
- 変数番号 列の
- ただし、同じタイプのすべての列
double precision(エイリアスfloat8)
ARRAYを使用する この場合に入力して、可変数の値をネストします。さらに、列名を含む配列を返します:
CREATE OR REPLACE FUNCTION data_of(_id integer)
RETURNS TABLE (datahora timestamp, names text[], values float8[])
LANGUAGE plpgsql AS
$func$
DECLARE
_sensors text := 'col1, col2, col3'; -- plain list of column names
_type text := 'foo';
BEGIN
RETURN QUERY EXECUTE format('
SELECT datahora
, string_to_array($1) -- AS names
, ARRAY[%s] -- AS values
FROM %s
WHERE id = $2
ORDER BY datahora'
, _sensors, _type)
USING _sensors, _id;
END
$func$;
さまざまな完全なテーブルタイプ
実際にテーブルのすべての列を返す 、ポリモーフィックタイプを使用したシンプルで強力なソリューションがあります :
CREATE OR REPLACE FUNCTION data_of(_tbl_type anyelement, _id int)
RETURNS SETOF anyelement
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE format('
SELECT *
FROM %s -- pg_typeof returns regtype, quoted automatically
WHERE id = $1
ORDER BY datahora'
, pg_typeof(_tbl_type))
USING _id;
END
$func$;
電話(重要!):
SELECT * FROM data_of(NULL::pcdmet, 17);
pcdmetを置き換えます 他のテーブル名との通話で。
これはどのように機能しますか?
anyelement は疑似データ型、ポリモーフィック型、非配列データ型のプレースホルダーです。 anyelementのすべての出現 関数内で、実行時に提供されたものと同じタイプに評価されます。定義された型の値を関数の引数として指定することにより、戻り型を暗黙的に定義します。
PostgreSQLは、作成されたすべてのテーブルに対して行タイプ(複合データ型)を自動的に定義するため、すべてのテーブルに対して明確に定義されたタイプがあります。これには、アドホックな使用に便利な一時テーブルが含まれます。
どのタイプもNULLにすることができます 。 NULLを提出する 値、テーブルタイプにキャスト: NULL::pcdmet 。
これで、関数は明確に定義された行タイプを返し、SELECT * FROM data_of()を使用できます。 行を分解して個々の列を取得します。
pg_typeof(_tbl_type) テーブルの名前をオブジェクト識別子タイプregtypeとして返します 。自動的にtextに変換される場合 、識別子は自動的に二重引用符で囲まれ、スキーマ修飾されます 必要に応じて、SQLインジェクションから自動的に防御します。これは、quote_ident()でスキーマ修飾されたテーブル名を処理することもできます。 失敗します。参照:
- PostgreSQL関数パラメータとしてのテーブル名