art*_*hur 3 postgresql insert plpgsql functions record
我正在开发一个带有两个参数的用户定义函数:
create or replace function gesio(
events_table_in regclass,
events_table_out regclass)
returns void as $$ ... $$
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
我怎样才能保存rec到events_table_out?
Erw*_*ter 10
这里是只有PL / pgSQL的一个解决方案。也很简单,优雅。不过,相当先进的东西。
需要 Postgres 9.0或更高版本(可能是旧版本的解决方法)。
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;
Run Code Online (Sandbox Code Playgroud)
打电话(重要!):
SELECT gesio(NULL::t, 't1');
Run Code Online (Sandbox Code Playgroud)
t并且t1是具有相同架构的表。
请注意,anyelement只有在函数体中的计算需要它的值或数据类型时,才需要多态参数 ( )。否则,您可以像后面的答案中演示的那样进行简化:
FOR循环的隐式游标而不是显式游标。这通常是可取的。
需要克服的一个障碍是函数内部的变量还不能定义为多态类型anyelement(目前)。SO上的这个相关答案解释了解决方案。也为旧版本提供了解决方法。
我正在提交一个NULLtype 值t,它有三个目的:
第一个参数的值被丢弃。使用NULL.
考虑这个有关 SO 的相关答案并提供更多详细信息。最有趣的部分是最后一章各种完整的表格类型。
如果您的计算不是太复杂,您可以用单个动态 SQL 语句替换循环,这通常更快。
不幸的是,RECORD使用 PL/pgSQL解析类型并不容易。如果参数中传递的表的结构始终与其他表或类型相同,则可以直接使用此类型而不是RECORD,然后使用以下内容:
DECLARE
recCurs table_or_type;
...
BEGIN
...
OPEN recCurs FOR EXECUTE ...
...
EXECUTE 'INSERT INTO ' || events_table_out || ' VALUES(($1).*)'
USING recCurs;
...
Run Code Online (Sandbox Code Playgroud)
但这不适用于RECORD类型。我能想到的唯一解决方案是手动创建查询。但是 PL/pgSQL 无法动态获取RECORD类型的键。所以你必须使用一些外部工具。最好的(在我看来)这种工作是hstore扩展。安装后,您可以在您的数据库上创建它(以下仅适用于 9.1+,之前您应该手动完成):
CREATE EXTENSION hstore;
Run Code Online (Sandbox Code Playgroud)
现在。您可以为转换RECORD类型的hstore类型,使用hstore(recCurs),所以你可以动态地遍历它的键和值与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 || ')';
...
Run Code Online (Sandbox Code Playgroud)
当然,它只有在“指向”的表events_table_out具有所有具有的列events_table_in(第一个可以具有更多列)时才有效。
RESUMING:在 PL/pgSQL 上总是想要一些动态的键/值数据类型RECORD,这hstore还不够,可以使用。