web-dev-qa-db-ja.com

レコード変数からテーブルに値を挿入する

2つの引数を取るユーザー定義関数を開発しています。

create or replace function gesio(
    events_table_in regclass,  
    events_table_out regclass)
returns void as $$ ... $$

events_table_inevents_table_outのスキーマはまったく同じです。

簡単に説明すると、私はevents_table_inのレコードをループして、レコードを操作し、次の方法でevents_table_outに操作したレコードを追加(挿入)したいと思います。

OPEN recCurs FOR execute 
format('SELECT * FROM %s order by session_id, event_time', event_table_in);

LOOP
    FETCH recCurs into rec;
    if not found then
      exit;
    end if;

    -- 1. do something with rec

    -- 2. insert the rec into events_table_out

end loop;

recevents_table_outに保存するにはどうすればよいですか?

3
arthur

ありますis PL/pgSQLだけのソリューション。シンプルでエレガント。ただし、かなり高度なものです。
Postgres 9.以降が必要(古いバージョンの回避策が可能)。

CREATE OR REPLACE FUNCTION gesio(_tbl_in anyelement, _tbl_out regclass)
  RETURNS void AS
$func$
BEGIN

FOR _tbl_in IN EXECUTE
   format('SELECT * FROM %s', pg_typeof(_tbl_in))
LOOP
   -- do something with record

   EXECUTE format('INSERT INTO %s SELECT $1.*', _tbl_out)
   USING _tbl_in;
END LOOP;

END
$func$  LANGUAGE plpgsql;

電話(重要!):

SELECT gesio(NULL::t, 't1');

tおよびt1は同じスキーマのテーブルです。

Note多相パラメータ(anyelement)は、関数本体での計算に値またはデータ型が必要な場合にのみ必要であることを示しています。そうでなければ、この後の回答で示されているように単純化できます。

主な成分

克服すべき障害は、関数内の変数が多相型anyelement(まだ)として定義できないことです。 SOに関するこの関連回答 はソリューションを説明しています。 古いバージョンの回避策も提供します。

タイプNULLt値を渡します。これは3つの目的を果たします。

  • テーブル名を入力してください。
  • テーブルタイプを指定します。
  • ループ変数として機能します。

最初のパラメーターのvaluediscardedです。 NULLを使用します。

考えてみてください SOに関する詳細)に関するこの関連する回答 。最も興味深い部分は、最後の章さまざまな完全なテーブルタイプです。

SQL Fiddleデモ。

計算があまり洗練されていない場合は、ループを単一の動的SQLステートメントで置き換えることができます。これは通常、より高速です。

9

残念ながら、PL/pgSQLを使用してRECORD型を解析するのは簡単ではありません。引数で渡されるテーブルの構造が他のテーブルまたはタイプと常に同じである場合は、RECORDの代わりにこのタイプを直接使用して、以下を使用できます。

DECLARE
    recCurs table_or_type;
...
BEGIN
...
OPEN recCurs FOR EXECUTE ...
...
EXECUTE 'INSERT INTO ' || events_table_out || ' VALUES(($1).*)'
        USING recCurs;
...

ただし、これはRECORDタイプでは機能しません。私が考えることができる唯一の解決策は、手動でクエリを作成することです。しかし、PL/pgSQLはRECORD型のキーを動的に取得する方法を提供しません。したがって、いくつかの外部ツールを使用する必要があります。この種の仕事に最適な(私の意見では) hstore extension です。インストールしたら、データベース上に作成できます(以下は9.1以降でのみ機能します。以前のバージョンでは手動で作成する必要があります)。

CREATE EXTENSION hstore;

今。 hstore(recCurs)を使用すると、RECORD型をhstore型に変換できるため、each関数を使用してキーと値を動的に反復できます。

DECLARE
   recCurs record;
   kv record;
   v_cols text;
   v_vals text;
BEGIN
...
    OPEN recCurs FOR EXECUTE ...
...
    -- 1. do something with rec

    -- 2. insert the rec into events_table_out:
    v_cols := '';
    v_vals := '';
    FOR kv IN SELECT * FROM each(hstore(recCurs)) LOOP
        v_cols := v_cols || kv.key || ',';
        v_vals := v_vals || quote_nullable(kv.value) || ',';
    END LOOP;
    v_cols := substr(v_cols, 1, length(v_cols)-1);
    v_vals := substr(v_vals, 1, length(v_vals)-1);
    EXECUTE 'INSERT INTO ' || events_table_out
            || '(' || v_cols || ') '
            || 'VALUES (' || v_vals || ')';
...

もちろん、これは、events_table_outが「指す」テーブルが、events_table_inが持つすべての列を持っている場合にのみ機能します(最初の列はより多くの列を持つことができます)。

RESUMING:常にPL/pgSQLで何らかの動的キー/値のデータ型が必要であり、RECORDでは不十分で、hstoreを使用できます。

5
MatheusOl