从PostgreSQL表中导出特定行作为INSERT SQL脚本

289

我有一个名为nyummy的数据库模式和一个名为cimory的表:

create table nyummy.cimory (
  id numeric(10,0) not null,
  name character varying(60) not null,
  city character varying(50) not null,
  CONSTRAINT cimory_pkey PRIMARY KEY (id)
);

我想将表中城市为“tokyo”的记录/数据导出为插入SQL脚本文件。请问如何做到?无论是免费的GUI工具还是命令行都可以(虽然GUI工具的解决方案更好)。我尝试了pgAdmin III,但找不到这个选项。


PostgreSQL无法跨数据库选择。 至少在旧版本中不能,Greenplum也不能,不清楚9.x。 - PhilHibbs
我知道这已经过时了,但我想提一下,使用 dblink 是可以跨数据库选择的,它至少从 v8.3 开始就可用。它利用外部服务器和外部数据包装器连接到“远程”数据库。无论这些数据库存在于同一实例还是完全不同的主机上,都可以使用此方法。我已经广泛地使用它来创建材料化视图,以便促进某些报告等,并且它非常有效。 - G_Hosa_Phat
12个回答

412

创建一个包含你要导出的集合的表格,然后使用命令行实用程序pg_dump将其导出到一个文件中:

create table export_table as 
select id, name, city
from nyummy.cimory
where city = 'tokyo'
$ pg_dump --table=export_table --data-only --column-inserts my_database > data.sql

--column-inserts将以带有列名的插入命令形式进行转储。

--data-only不会转储模式。

正如下面的评论所述,创建视图而不是表将避免在需要新的导出时创建表。


3
好的,迄今为止你的解决方案有效。有一件事情被忽略了,我需要添加“-U用户名”。我几乎成功使用ToraSQL工具,只是在脚本结果中日期时间数据出现了错误。如果在两天内没有人能够提供GUI工具解决方案,你的答案将被接受。 - null
2
只是想与其他人分享,你也可以使用这个免费的GUI工具:SQL Workbench/J(带有postgreSQL jdbc4驱动程序),来完成同样的事情。 - null
3
如果使用create view export_view...,就会更好,因为视图会随着基础表的更改而保持最新。文档中提到--table=table: Dump only tables (or **views**...,所以我有些希望它能够工作,但是导出视图时却没有数据。 :P - poshest
1
@poshest 在我的9.5版本中可以正常工作。你具体尝试了什么? - Clodoaldo Neto
1
这个解决方案可行。根据这个 SQL 文件,表名为 "export_table"。但原始表名是 nyummy.cimory。我们拿生产数据库和 UAT 数据库服务器来说。在两个数据库服务器上我都有 nyummy 模式和 cimory 表。我需要从生产 cimory 表中获取特定行的转储数据到 UAT cimory 表中。为此,我需要创建一个 "export_table" 表,然后更改 sql 文件中的表名。然后我需要复制数据到 UAT 表中。所以有没有办法从表中获取特定的行转储数据? - Nivetha Jaishankar
显示剩余7条评论

232

要仅导出数据,请使用COPY

COPY (SELECT * FROM nyummy.cimory WHERE city = 'tokio') TO '/path/to/file.csv';
你可以像示例演示的那样,导出整个表、仅选定列或查询结果。不需要显式创建表格。
您将获得一个每行为一个表行的文件作为纯文本(而不是INSERT命令)。比INSERT命令更小、更快。
要将其导入到匹配结构的另一个Postgres表中(列顺序相同,数据类型兼容!):
COPY other_tbl FROM '/path/to/file.csv';

COPY 命令用于读写服务器本地文件,而不是像 pg_dump 或者 psql 这样的客户端程序读写客户端本地文件。如果两个程序都在同一台机器上运行,则没有太大区别,但对于远程连接而言则有很大差异。

此外,psql 还提供了 \copy 命令:

执行前端(客户端)拷贝。这是一个运行 SQL COPY命令的操作, 但与服务器读取或写入指定文件不同,psql 会读取或写入该文件,并在服务器和本地文件系统之间路由数据。这意味着文件可访问性和权限属于本地用户,而不是服务器,也不需要任何 SQL 超级用户权限

使用与上述相同的语法。只需将 COPY 替换为 \copy 即可。


27
楼主特别提到了“以数据形式为插入SQL脚本文件”,我猜他说的是insert命令,你也这么认为吧? - Clodoaldo Neto
2
@Clodoaldo:你可能是对的,如果是这样,你的答案会更合适。也可以单独在pgAdmin中复制CREATE脚本(正如OP提到的GUI)。 - Erwin Brandstetter
5
可以使用STDINSTDOUT代替文件路径,对于小型数据导出非常有用。 - Amir Ali Akbari
2
没有 --column-inserts 标志,pg_dump 在生成的 SQL 代码中为每个表使用 STDIN 的 COPY - Randall
4
请注意,您选择的列的顺序必须与目标数据库中列的顺序相匹配。如果不匹配,这可能会导致操作失败,或者更糟糕的是成功插入错误的数据。 - Nathan Wallace
显示剩余3条评论

61

这是一种使用pgAdmin手动将表格导出为脚本的简单快捷方法,无需额外安装:

  1. 右键点击目标表并选择“备份”。
  2. 选择文件路径以存储备份。作为格式选择“纯文本(Plain)”。
  3. 在底部打开“Dump Options #2”选项卡并勾选“Use Column Inserts”。
  4. 点击备份按钮。
  5. 如果您使用文本阅读器(例如notepad ++)打开生成的文件,您将获得创建整个表格的脚本。从那里,您只需复制生成的INSERT语句即可。

该方法也适用于像@Clodoaldo Neto答案中演示的导出表的技术。

点击目标表的右键并选择“备份”

选择目标路径并更改格式为“纯文本(Plain)”

在底部打开“Dump Options #2”选项卡并勾选“Use Column Inserts”

您可以从那里复制INSERT语句。


当我这样做时,没有“备份”选项。这是pgAdmin III v1.18.1连接到基于PostgreSQL 8.2.15的Greenplum 4.3.4.1。 - PhilHibbs
我安装了pgAdmin III v1.18.1,发现有“备份”选项。我连接的是PostgreSQL 9.5。所以问题很可能是pgAdmin和Greenplum之间的联系。 - Andi R
在pgAdmin4中按预期工作 - Nikhil
运行得非常完美,感谢您发布这个选项! - jpierson

12

根据我的使用情况,我能够简单地将内容管道传输到grep命令中。

pg_dump -U user_name --data-only --column-inserts -t nyummy.cimory | grep "tokyo" > tokyo.sql

4
必须考虑在其他领域中涉及“东京”这个问题。 - Buyut Joko Rivai
@BuyutJokoRivai,由于它通常只是一个表格转储,所以大多数情况下应该没问题。 - Ismail Iqbal
尽管使用大表,您将为grep转储所有行,这是解决方案的陷阱情况。然后,我们查询和存储结果到表中以转储,如此处所示https://dev59.com/Smcs5IYBdhLWcg3wfD9P#12816187更适合。 - Nam G VU

9

SQL Workbench拥有这样一个功能。

运行查询后,在查询结果上右键单击,选择“复制数据为 SQL > SQL 插入”


1
它运行得很好。当你选择'postgres'作为'driver'时,很可能你必须自己下载JDBC驱动程序: https://jdbc.postgresql.org/download.html (它是一个.jar文件 - Java二进制文件),并将其添加为postgresql连接的'driver'。连接字符串(或在界面中称为URL)应该看起来像这样:jdbc:postgresql://127.0.0.1:5432/db_name - mrmuggles
DBVisualizer具有类似且出色的功能,可以将内容复制到文件或直接复制到剪贴板。 - Noumenon

8

我试着按照@PhilHibbs的代码,以不同的方式编写了一个过程来实现这个功能。请查看并测试。

 CREATE OR REPLACE FUNCTION dump(IN p_schema text, IN p_table text, IN p_where text)
   RETURNS setof text AS
 $BODY$
 DECLARE
     dumpquery_0 text;
     dumpquery_1 text;
     selquery text;
     selvalue text;
     valrec record;
     colrec record;
 BEGIN

     -- ------ --
     -- GLOBAL --
     --   build base INSERT
     --   build SELECT array[ ... ]
     dumpquery_0 := 'INSERT INTO ' ||  quote_ident(p_schema) || '.' || quote_ident(p_table) || '(';
     selquery    := 'SELECT array[';

     <<label0>>
     FOR colrec IN SELECT table_schema, table_name, column_name, data_type
                   FROM information_schema.columns
                   WHERE table_name = p_table and table_schema = p_schema
                   ORDER BY ordinal_position
     LOOP
         dumpquery_0 := dumpquery_0 || quote_ident(colrec.column_name) || ',';
         selquery    := selquery    || 'CAST(' || quote_ident(colrec.column_name) || ' AS TEXT),';
     END LOOP label0;

     dumpquery_0 := substring(dumpquery_0 ,1,length(dumpquery_0)-1) || ')';
     dumpquery_0 := dumpquery_0 || ' VALUES (';
     selquery    := substring(selquery    ,1,length(selquery)-1)    || '] AS MYARRAY';
     selquery    := selquery    || ' FROM ' ||quote_ident(p_schema)||'.'||quote_ident(p_table);
     selquery    := selquery    || ' WHERE '||p_where;
     -- GLOBAL --
     -- ------ --

     -- ----------- --
     -- SELECT LOOP --
     --   execute SELECT built and loop on each row
     <<label1>>
     FOR valrec IN  EXECUTE  selquery
     LOOP
         dumpquery_1 := '';
         IF not found THEN
             EXIT ;
         END IF;

         -- ----------- --
         -- LOOP ARRAY (EACH FIELDS) --
         <<label2>>
         FOREACH selvalue in ARRAY valrec.MYARRAY
         LOOP
             IF selvalue IS NULL
             THEN selvalue := 'NULL';
             ELSE selvalue := quote_literal(selvalue);
             END IF;
             dumpquery_1 := dumpquery_1 || selvalue || ',';
         END LOOP label2;
         dumpquery_1 := substring(dumpquery_1 ,1,length(dumpquery_1)-1) || ');';
         -- LOOP ARRAY (EACH FIELD) --
         -- ----------- --

         -- debug: RETURN NEXT dumpquery_0 || dumpquery_1 || ' --' || selquery;
         -- debug: RETURN NEXT selquery;
         RETURN NEXT dumpquery_0 || dumpquery_1;

     END LOOP label1 ;
     -- SELECT LOOP --
     -- ----------- --

 RETURN ;
 END
 $BODY$
   LANGUAGE plpgsql VOLATILE;

然后:
-- for a range
SELECT dump('public', 'my_table','my_id between 123456 and 123459'); 
-- for the entire table
SELECT dump('public', 'my_table','true');

我在我的Postgres 9.1上测试了一个混合字段数据类型的表格(文本,双精度浮点数,整数,没有时区的时间戳等)。

这就是为什么需要在TEXT类型中进行CAST。

我的测试运行了约9百万行,看起来它在运行了大约18分钟之前失败了。

附注:我在网上找到了一个MySQL的等效方法。


5
您可以通过指定记录的视图,然后转储 SQL 文件来查看表格。
CREATE VIEW foo AS
SELECT id,name,city FROM nyummy.cimory WHERE city = 'tokyo'

3
我在使用pgAdmin III时尝试过,但对于视图对象,没有转储选项。 - null
试试Navicat吧,我正在使用它,它有导出SQL脚本的选项。 - Giorgi Peikrishvili
@Giorgi:有免费版本吗? - null
使用Postgres 9.1是不可能的。 - hcarrasko

2

我需要一种方法,在不创建临时表(在生产环境中)的情况下生成插入语句。我看到了一些有用的参数来完成上面的pg_dump操作,但最终还是想出了一种

生成插入语句并将其转储到文件中的方法。

下面的语句解决了这个问题,我觉得对于那些遇到类似问题的人会很有用。

$ /usr/local/bin/pg_dump -h the_db_host_address -d the_db_name --table=schema_name.table_name --data-only --column-inserts -U postgres -p the_port_number -v -f /Users/the_user/folder_name/insrt_stmts_file_name.sql

下一步是提示输入密码,上面的例子中我提供了postgres 的密码,但是任何具有读取权限的其他用户都可以正常工作。

我在 DBeaver 客户端中运行了名为 insrt_stmts_file_name.sql 的文件中的插入语句。

但是也可以使用以下脚本从命令提示符中运行相同的操作:

/usr/local/bin/psql -h the_db_host_address -d the_db_name -v -U postgres -f /Users/the_user/folder_name/insrt_stmts_file_name.sql

pg_dump / psql 标志的参考: -h = 主机 -d = 数据库名称 -v = 详细模式(它会在进程中输出) -U = 数据库用户名 -f = 文件/路径


1
我刚刚编写了一个快速过程来完成这个任务。它仅适用于单行,因此我创建了一个临时视图只选择我想要的行,然后用我要插入的实际表替换pg_temp.temp_view。
CREATE OR REPLACE FUNCTION dv_util.gen_insert_statement(IN p_schema text, IN p_table text)
  RETURNS text AS
$BODY$
DECLARE
    selquery text; 
    valquery text; 
    selvalue text; 
    colvalue text; 
    colrec record;
BEGIN

    selquery := 'INSERT INTO ' ||  quote_ident(p_schema) || '.' || quote_ident(p_table);

    selquery := selquery || '(';

    valquery := ' VALUES (';
    FOR colrec IN SELECT table_schema, table_name, column_name, data_type
                  FROM information_schema.columns 
                  WHERE table_name = p_table and table_schema = p_schema 
                  ORDER BY ordinal_position 
    LOOP
      selquery := selquery || quote_ident(colrec.column_name) || ',';

      selvalue := 
        'SELECT CASE WHEN ' || quote_ident(colrec.column_name) || ' IS NULL' || 
                   ' THEN ''NULL''' || 
                   ' ELSE '''' || quote_literal('|| quote_ident(colrec.column_name) || ')::text || ''''' || 
                   ' END' || 
        ' FROM '||quote_ident(p_schema)||'.'||quote_ident(p_table);
      EXECUTE selvalue INTO colvalue;
      valquery := valquery || colvalue || ',';
    END LOOP;
    -- Replace the last , with a )
    selquery := substring(selquery,1,length(selquery)-1) || ')';
    valquery := substring(valquery,1,length(valquery)-1) || ')';

    selquery := selquery || valquery;

RETURN selquery;
END
$BODY$
  LANGUAGE plpgsql VOLATILE;

被如此调用:
SELECT distinct dv_util.gen_insert_statement('pg_temp_' || sess_id::text,'my_data') 
from pg_stat_activity 
where procpid = pg_backend_pid()

我还没有测试过这个代码是否能抵御注入攻击,请告诉我如果quote_literal函数不足以实现该功能。

此外,它只适用于可以简单地转换为::text并再次转换回来的列。

此外,这是针对Greenplum的,但我想不出为什么它不能在Postgres上运行,如有错误请指正。


0

只是想补充一种简单但手动的方法。

1)使用PGADMIN 4,在查询数据后,将数据下载为csv文件。

  1. 在任何记事本中打开csv文件,然后将数据复制并粘贴到在线csv转换为sql的工具中。例如:https://www.convertcsv.com/csv-to-sql.htm。 您可以设置目标表的名称。输出是sql插入脚本。

  2. 将INSERT脚本复制回PGADMIN4中的目标数据库查询窗口。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接