将记录变量中的值插入表中

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_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;
Run Code Online (Sandbox Code Playgroud)

我怎样才能保存recevents_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只有在函数体中的计算需要它的值或数据类型时,才需要多态参数 ( )。否则,您可以像后面的答案中演示的那样进行简化:

主要成分

需要克服的一个障碍是函数内部的变量还不能定义为多态类型anyelement(目前)。SO上的这个相关答案解释了解决方案。也为旧版本提供了解决方法

我正在提交一个NULLtype 值t,它有三个目的:

  • 提供表名。
  • 提供表类型。
  • 作为循环变量。

第一个参数的丢弃。使用NULL.

考虑这个有关 SO 的相关答案并提供更多详细信息。最有趣的部分是最后一章各种完整的表格类型

SQL 小提琴演示。

如果您的计算不是太复杂,您可以用单个动态 SQL 语句替换循环,这通常更快。


Mat*_*sOl 5

不幸的是,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还不够,可以使用。