sql >> データベース >  >> RDS >> PostgreSQL

PL / pgSQL関数をリファクタリングして、さまざまなSELECTクエリの出力を返します

    動的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のような構造化されていないドキュメントデータ型を使用できます。 、jsonbhstore または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関数パラメータとしてのテーブル名


    1. Visual C#2008でのtnsnames.oraの解析

    2. SqlAlchemyとcx_Oracleを使用してPandasDataFrameをOracleデータベースに書き込む場合は、to_sql()を高速化します

    3. VDP AdvancedSQLAgentを使用したSQLデータベースのバックアップ

    4. PostgreSQL 11:新機能