【问题标题】:Python psql \copy CSV to remote serverPython psql \copy CSV 到远程服务器
【发布时间】:2018-03-27 07:10:19
【问题描述】:

我正在尝试使用 python 3.6 将 csv(具有标题和引号字符“)复制到远程 postgres 10 服务器上的表中。它是一个大型 CSV(250 万行,800MB),而我之前导入将其放入数据帧,然后使用 dataframe.to_sql,这非常占用内存,所以我改用 COPY。

将 COPY 与 psycopg2 或 sqlalchemy 一起使用可以正常工作,但远程服务器无权访问本地文件系统。

在终端中使用 psql 我已成功运行下面的查询来填充表。我认为 psycopg2 或 sqlalchemy 无法使用 \copy。

\copy table (col1, col2) FROM file_location CSV HEADER QUOTE '"' NULL ''

但是,当我尝试使用如下所示的单行 psql -c 命令时,它不起作用并且出现错误:

错误:复制引号必须是单个单字节字符。

psql -U user -h ip -d db -w pw -c "\copy table (col1, col2) FROM file_location CSV HEADER QUOTE '"' NULL ''"

你能告诉我为什么会这样吗?

这个单行 -c psql 语句在 python 中使用 subprocess 模块实现比打开终端并执行我不知道该怎么做的命令更容易。如果您能提出解决方法或不同的方法,那就太好了。

====== 根据 Andrew 的建议,在命令行上转义引号字符。然而,当像下面这样在 python 中实现它时,会出现一个新的错误:

/bin/sh: -c: line 0: 寻找匹配的'''时出现意外的EOF

/bin/sh: -c: line 1: 语法错误:文件意外结束

"\"\copy table (col1, col2) FROM file_location CSV HEADER QUOTE '\"' NULL ''\""
cmd = f'psql -U {user} -h {ip} -d {db} -w {pw} -c {copy_statement}'
subprocess.call(cmd, shell=True)

【问题讨论】:

  • 两台电脑之间有网络连接吗?
  • 当我将本地代码库转换为使用 docker 时出现这种情况。使用 docker compose 有两个容器(python3.6 conda 和 postgis)。如果你能建议如何配置 docker-compose,我可以坚持原来的 COPY?是不是开个港口那么简单?将 COPY 与 docker 一起使用时的错误说“文件不存在”(与 postgis 服务器容器有关)。
  • 你可以使用bat文件运行,使用成功的方法执行终端命令
  • 感谢您的建议。我在 Mac 上,但是是的,做一个 .sh 可能是要走的路,或者我可以将该步骤添加到 python 容器的 Dockerfile
  • 是外壳转义问题吗?尝试使用 '\"' 作为 QUOTE 参数。

标签: python postgresql csv docker-compose psycopg2


【解决方案1】:

如果可以避免的话,尽量不要使用shell=True。最好自己标记命令以帮助 sh。

subprocess.call(["psql", "-U", "{user}", "-h", "{ip}", "-d", "{db}", "-w", "{pw}", "-c", "{copy statement}"])

在这种情况下,您的复制语句可能会被逐字传递给 psql,因为没有需要考虑的 shell 引用问题。 (注意,python 仍然需要引用这个,所以字符串会保持原样)。


如果您仍然想使用shell=True,那么您必须为python shell 转义字符串文字

"\"\copy table (col1, col2) FROM file_location CSV HEADER QUOTE '\\\"' NULL ''\""

将在python中创建一个字符串

"\copy table (col1, col2) FROM file_location CSV HEADER QUOTE '\"' NULL ''\"

我们发现我们首先需要在我们的外壳上使用它!


编辑(澄清来自 cmets 的内容)

subprocess.call,当不使用shell=True 时,接受一个可迭代的参数。

所以你可以拥有

psql_command = "\"\copy table (col1, col2) FROM file_location CSV HEADER QUOTE '\\\"' NULL ''\""
# user, hostname, password, dbname all defined elsewhere above.
command = ["psql",
    "-U", user,
    "-h", hostname,
    "-d", dbname,
    "-w", password,
    "-c", psql_command,
]

subprocess.call(command)

https://docs.python.org/2/library/subprocess.html#subprocess.callhttps://docs.python.org/3/library/subprocess.html#subprocess.call

额外编辑:- 请注意,为避免 shell 注入,您应该使用此处描述的方法。见https://docs.python.org/2/library/subprocess.html#frequently-used-arguments的警告部分

【讨论】:

  • 自己标记化是指在 subprocess.call 中手动构建查询?所以没有变量,只有字符串?在这种情况下,您的解决方案有效,但这意味着我必须对字符串中的变量进行硬编码,这不是我的目标。
  • @user578582 - 您仍然可以使用变量 - subprocess.call 将标记列表作为其第一个参数,因此您可以使用任意字符串数组。我会用一个例子来更新答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-09
  • 2016-03-11
  • 2018-05-31
  • 2014-11-18
  • 1970-01-01
  • 2017-01-21
  • 1970-01-01
相关资源
最近更新 更多