12

我有大量数据文件要处理并存储在远程数据库中。数据文件的每一行代表数据库中的一行,但必须在插入数据库之前进行格式化。

我的第一个解决方案是通过编写 bash 脚本来处理数据文件并生成 SQL 数据文件,然后将转储 SQL 文件导入数据库。这个解决方案似乎太慢了,正如您所见,涉及创建中间 SQL 文件的额外步骤。

我的第二个解决方案是编写 bash 脚本,在处理数据文件的每一行时,创建并INSERT INTO ...声明 SQL 语句并将其发送到远程数据库:

echo sql_statement | psql -h remote_server -U username -d database

即不创建SQL 文件。然而,这个解决方案有一个我正在寻找建议的主要问题:
每次我必须重新连接到远程数据库以插入一行。

有没有办法连接到远程数据库,保持连接,然后“管道”或“发送”插入 SQL 语句而不创建巨大的 SQL 文件?

4

2 回答 2

20

回答你的实际问题

的。您可以使用命名管道而不是创建文件。考虑以下演示。

x在我的数据库中创建一个模式event进行测试:

-- DROP SCHEMA x CASCADE;
CREATE SCHEMA x;
CREATE TABLE x.x (id int, a text);

从 shell 创建一个命名管道(fifo),如下所示:

postgres@db:~$ mkfifo --mode=0666 /tmp/myPipe

1 )使用服务器上的COPY命名管道调用 SQL 命令:

postgres@db:~$ psql event -p5433 -c "COPY x.x FROM '/tmp/myPipe'"

这将获得数据库中表的排他锁。x.x连接保持打开状态,直到 fifo 获取数据。小心不要让这个打开太久!您可以在填充管道调用它以最大限度地减少阻塞时间。您可以选择事件的顺序。该命令在两个进程绑定到管道后立即执行。第一个等待第二个。

或者2)您可以从客户端上的管道执行 SQL :

postgres@db:~$ psql event -p5433 -f /tmp/myPipe

这更适合您的情况。此外,在 SQL 被一次性执行之前不会锁定表。

Bash 会出现阻塞。它正在等待输入到管道。要从一个 bash 实例完成所有操作,您可以将等待进程发送到后台。像这样:

postgres@db:~$ psql event -p5433 -f /tmp/myPipe 2>&1 &

无论哪种方式,从同一个 bash 或不同的实例,您现在都可以填充管道变体1)
的三行演示:

postgres@db:~$ echo '1  foo' >> /tmp/myPipe; echo '2    bar' >> /tmp/myPipe; echo '3    baz' >> /tmp/myPipe;

(注意使用制表符作为分隔符或使用COPY指示接受不同的分隔符WITH DELIMITER 'delimiter_character'
这将使用 COPY 命令触发挂起的 psql 执行并返回:

COPY 3

变体2)的演示:

postgres@db:~$ (echo -n "INSERT INTO x.x VALUES (1,'foo')" >> /tmp/myPipe; echo -n ",(2,'bar')" >> /tmp/myPipe; echo ",(3,'baz')" >> /tmp/myPipe;)

INSERT 0 3

完成后删除命名管道:

postgres@db:~$ rm /tmp/myPipe

检查成功:

event=# select * from x.x;
 id |         a
----+-------------------
  1 | foo
  2 | bar
  3 | baz

上面代码的有用链接

使用命名管道使用 postgres 读取压缩文件 命名管道
简介
在后台运行 bash 脚本的最佳实践


您可能需要或不需要的建议

对于批量,您有比每行INSERT单独插入更好的解决方案。使用此语法变体:

INSERT INTO mytable (col1, col2, col3) VALUES
 (1, 'foo', 'bar')
,(2, 'goo', 'gar')
,(3, 'hoo', 'har')
...
;

将您的陈述写入文件并INSERT像这样做一个质量:

psql -h remote_server -U username -d database -p 5432 -f my_insert_file.sql

(5432 或 db-cluster 正在侦听的任何端口)
my_insert_file.sql可以容纳多个 SQL 语句。事实上,像这样恢复/部署整个数据库是很常见的做法。请查阅有关参数的手册-f,或在 bash:中查阅man psql

或者,如果您可以将(压缩)文件传输到服务器,则可以使用COPY更快地插入(解压缩)数据。

您还可以在 PostgreSQL 中进行部分或全部处理。为此,您可以COPY TO(或INSERT INTO)一个临时表并使用普通 SQL 语句来准备并最终插入/更新您的表。我经常这样做。请注意,临时表在会话中生死攸关。

您可以使用像pgAdmin这样的 GUI来进行舒适的处理。SQL 编辑器窗口中的会话保持打开状态,直到您关闭该窗口。(因此,临时表会一直存在,直到您关闭窗口。)

于 2011-10-21T14:16:22.617 回答
1

我知道我迟到了,但你为什么不能把你所有的INSERT陈述合并成一个字符串,用分号标记每个陈述的结尾?(警告!前面的伪代码......)

代替:

for each line
  sql_statement="INSERT whatever YOU want"
  echo $sql_statement | psql ...
done

利用:

sql_statements=""
for each line
  sql_statement="INSERT whatever YOU want;"
  sql_statements="$sql_statements $sql_statement"
done
echo $sql_statements | psql ...

这样你就不必在你的文件系统上创建任何东西,做一堆重定向,在后台运行任何任务,记住之后删除文件系统上的任何东西,甚至提醒自己命名管道是什么。

于 2013-06-13T04:25:01.940 回答