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

PL/pgSQL関数から不明な列を持つ動的テーブルを返します

    SQLは呼び出し時にリターンタイプを知る必要があるため、これを解決するのは困難です。 。
    また、plpgsql関数には明確に定義されたリターンタイプが必要です。 。

    匿名のレコードを返すことを選択した場合 、あなたはあなたが定義したものを手に入れます:匿名の記録。 Postgresは中身を知りません。したがって、列定義リストは必須です。 タイプを分解します。

    正確な要件に応じて、さまざまな回避策があります。 呼び出し時にリターンタイプを知る方法がある場合 、多形タイプをお勧めします この回答の最後の章で概説されているように(「さまざまな完全なテーブルタイプ」):
    PL / pgSQL関数をリファクタリングして、さまざまなSELECTクエリの出力を返します

    ただし、関数内の実行時に戻り型に別の列を追加することはカバーしていません 。それは不可能です。 アプローチ全体を再考する

    現在のアプローチとして、私が考えることができる最も近いものは、2回目の呼び出しでクエリを実行する一時テーブル(またはカーソル)です。 単一のトランザクション内 。

    コードに他の問題がいくつかあります 。以下の注を参照してください。

    概念実証

    CREATE OR REPLACE FUNCTION f_tbl_plus_infowindow (_tbl regclass) -- regclass!
      RETURNS void AS  -- no direct return type
    $func$
    DECLARE
       -- appending _tmp for temp table
       _tmp text := quote_ident(_tbl::text || '_tmp');
    BEGIN
    
    -- Create temp table only for duration of transaction
    EXECUTE format(
       'CREATE TEMP TABLE %s ON COMMIT DROP AS TABLE %s LIMIT 0', _tmp, _tbl);
    
    IF EXISTS (
       SELECT 1
       FROM   pg_attribute a
       WHERE  a.attrelid = _tbl
       AND    a.attname  = 'infowindow'
       AND    a.attisdropped = FALSE)
    THEN
       EXECUTE format('INSERT INTO %s SELECT * FROM %s', _tmp, _tbl);
    ELSE
      -- This is assuming a NOT NULL column named "id"!
       EXECUTE format($x$
          ALTER  TABLE %1$s ADD COLUMN infowindow text;
          INSERT INTO %1$s
          SELECT *, 'ID: ' || id::text
          FROM   %2$s $x$
         ,_tmp, _tbl);
    END IF;
    
    END
    $func$ LANGUAGE plpgsql;
    

    呼び出しは単一のトランザクションで行う必要があります。クライアントによっては、明示的なトランザクションを開始する必要がある場合があります。

    BEGIN;
    SELECT f_tbl_plus_infowindow ('tbl');
    SELECT * FROM tbl_tmp;  -- do something with the returned rows
    ROLLBACK;               -- or COMMIT, does not matter here
    

    SQLフィドル。

    または、セッションの間、一時テーブルを存続させることもできます。ただし、繰り返し呼び出される名前の衝突には注意してください。

    メモ

    • 古いALIASの代わりにパラメータ名を使用してください コマンド。

    • 現在のスキーマを実際に「デフォルト」にするには、表示するより単純なクエリを使用します。 regclassの使用 トリックを自動的に行います。詳細:

      • PostgreSQL関数パラメータとしてのテーブル名

      さらに、これにより、構文エラーやSQLインジェクションの可能性も回避されます。 元のコードの非標準(または悪意を持って不正な形式)のテーブル名から。

    • ELSEのコード 句はまったく機能しません。

    • TABLE tbl; 基本的にはSELECT * FROM tbl;の略です。 。

    • format()の詳細 マニュアルで。




    1. オフセットとROW_NUMBER()

    2. Java-例外の最初の原因を見つける

    3. 演算子が存在しません:json =json

    4. PL/SQLのUPDATEの影響を受ける行数