2つの引数を取るユーザー定義関数を開発しています。
create or replace function gesio(
events_table_in regclass,
events_table_out regclass)
returns void as $$ ... $$
events_table_in
とevents_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;
rec
をevents_table_out
に保存するにはどうすればよいですか?
あります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に関するこの関連回答 はソリューションを説明しています。 古いバージョンの回避策も提供します。
タイプNULL
のt
値を渡します。これは3つの目的を果たします。
最初のパラメーターのvalueはdiscardedです。 NULL
を使用します。
考えてみてください SOに関する詳細)に関するこの関連する回答 。最も興味深い部分は、最後の章さまざまな完全なテーブルタイプです。
計算があまり洗練されていない場合は、ループを単一の動的SQLステートメントで置き換えることができます。これは通常、より高速です。
残念ながら、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
を使用できます。