動的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関数パラメータとしてのテーブル名