PL / pgSQL动态地从表复制数据

时间:2016-06-28 21:53:32

标签: postgresql plpgsql postgresql-9.4 string-interpolation

我试图根据information_schema中的内容编写一些SQL来复制给定数据库中所有PostgreSQL表中的数据。它应该将数据文件输出到我的本地机器,准备导入到另一台机器。最后,我要调整这个,以便我只转储表的选择部分(我倾倒的一些表有数百万条记录,我只想要一小部分数据用于测试目的)。 / p>

这是我到目前为止所拥有的......

--Copy all tables...
DO
$$
DECLARE
    formatstring text;
    rec record;
BEGIN
    RAISE NOTICE 'Copying tables...';
    formatstring = 'COPY (select * from %I) to ''C:\Media\Code\%s.csv'';';
    FOR rec IN 
        select table_name from information_schema.tables where table_schema = 'public' order by table_name
    LOOP
        RAISE NOTICE 'Table: %', rec.table_name;
        RAISE NOTICE format(formatstring, rec.table_name, rec.table_name);
        EXECUTE format(formatstring, rec.table_name, rec.table_name);
    END LOOP;
END;
$$
LANGUAGE plpgsql;

但是,我得到了这个例外...

ERROR:  unrecognized exception condition "format"
CONTEXT:  compilation of PL/pgSQL function "inline_code_block" near line 12
********** Error **********

ERROR: unrecognized exception condition "format"
SQL state: 42704
Context: compilation of PL/pgSQL function "inline_code_block" near line 12

单引号的转义似乎很好(已经检查了这个问题:Insert text with single quotes in PostgreSQL)。事实上,我可以执行以下操作,并将文本插入到格式中:

select format('COPY (select * from %I) to ''C:\Media\Code\%s.csv'';', 'system_user', 'system_user');

有人可以协助解决这个问题吗?我可以轻松地编写一个脚本或代码来为我生成复制命令,但是在一个简单的SQL中完成所有操作会很棒。

1 个答案:

答案 0 :(得分:1)

原因是您的第3 RAISE语句中出现语法错误。有several valid formats,但您无法直接将表达式提供给RAISE。它必须是一个字符串文字 - 可以选择字符串插值。

在参与其中时,我也会简化其他一些事情:

DO
$do$
DECLARE
   _formatstring text := $$COPY %1$I TO 'C:\Media\Code\%1$s.csv'$$;
   _sql text;
   _tbl text;
BEGIN
   RAISE NOTICE 'Copying tables...';
   FOR _tbl IN 
      SELECT table_name
      FROM   information_schema.tables
      WHERE  table_schema = 'public'
      ORDER  BY table_name
   LOOP
      _sql := format(_formatstring, _tbl);
      RAISE NOTICE 'Table: %', _tbl;
      RAISE NOTICE '%', _sql;  -- fixed!!
      EXECUTE _sql;
   END LOOP;
END
$do$ LANGUAGE plpgsql;

重点

  • 包含COPY而非SELECT * FROM tbl的普通表名称。
  • 使用嵌套的dollar-quotes
  • 格式说明符%1$I and %1$s for the format()函数,因此我们只需要提供一次表名。
  • 您可以在申报时分配变量。
  • 标量变量而不是record循环中的FOR - 我们只需要一列。