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

Posted

技术标签:

【中文标题】PL/pgSQL 从表中动态复制数据【英文标题】:PL/pgSQL to copy data from tables dynamically 【发布时间】:2016-06-28 21:53:32 【问题描述】:

我正在尝试编写一些 SQL 来根据 information_schema 中的内容从给定数据库中的所有 PostgreSQL 表中复制数据。它应该将数据文件输出到我的本地机器,准备好导入另一台机器。最终,我将对此进行调整,以便仅转储表的选定部分(我要转储的一些表有数百万条记录,我只需要一小部分数据用于测试目的)。

这是我目前所拥有的......

--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】:

原因是您的第三个 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() 函数,所以我们只需要提供一次表名。 您可以在声明时分配变量。 FOR 循环中的标量变量而不是 record - 无论如何我们只需要一列。

【讨论】:

出色的答案。非常感谢您的协助。我还有两个额外的问题:我想在输出文件名中包含 table_catalog,并且我想将基于条件语句的某些表限制为特定范围的数据,而不是整个表,仅适用于某些表。或者为了更好地解释,我想复制所有记录有限的静态数据表,但我只想要较大表中的数据子集。不过,我可以自己调查一下,或者问另一个问题。非常感谢您的帮助:) @ManoDestra:是的,这听起来像是另一个问题。评论太多了。您可以随时链接到这个以获取上下文。

以上是关于PL/pgSQL 从表中动态复制数据的主要内容,如果未能解决你的问题,请参考以下文章

PL/pgSQL 重启生成序列

PL/pgSQL:删除(更新)与记录匹配的行

关于 Pl/pgsql 中游标的一些错误

从表中复制数据并将其加载到另一个表中

写一个按钮从表中复制数据(Python3、Odoo 14)

如何从表中删除重复记录? [复制]