動的 SQL とRETURN
型
(最後に最良のものを保存しました。読み続けてください!)動的 SQL
を実行したいとします。原則として、それは plpgsql の助けを借りて簡単です。カーソルは必要ありません。実際、ほとんどの場合、明示カーソルを使用しないほうがよいでしょう。EXECUTE
遭遇する問題:まだ定義されていない type のレコードを返したい。関数は、その戻り値の型をRETURNS
句で (またはOUT
またはINOUT
パラメータで) 宣言する必要があります。あなたの場合、返される列の数、名前、およびタイプが異なるため、匿名レコードにフォールバックする必要があります。お気に入り:
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
ただし、この質問の目的のために、正しく型付けされ、名前が付けられた個々の列を可能な限り返したいと仮定しましょう。
固定リターン型のシンプルなソリューション
列は指定されているようです。データ型を想定し、名前とデータ型が異なる列が常に 2 つあるとdatahora
仮定します。timestamp
戻り値の型の一般的な名前を優先して放棄する名前。すべてのデータ
型を にキャストできるため、型も放棄し、すべてを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
。各列は type にキャストされtext
ます。
この種のコードは、 SQL インジェクションに対して非常に脆弱です。私はquote_ident()
それから保護するために使用します。変数内のいくつかの列名をひとまとめにすると_sensors
、 の使用が妨げられますquote_ident()
(通常は悪い考えです!)。quote_ident()
たとえば、代わりに列名を個別に実行するなど、他の方法で悪いものがそこにないことを確認してください。VARIADIC
パラメータが思い浮かびます...
PostgreSQL 9.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()
。見る: