如何使用 Postgres 中 CSV 文件中的值更新选定的行?
Posted
技术标签:
【中文标题】如何使用 Postgres 中 CSV 文件中的值更新选定的行?【英文标题】:How to update selected rows with values from a CSV file in Postgres? 【发布时间】:2012-02-13 04:38:47 【问题描述】:我正在使用 Postgres,并且想进行一个从 CSV 文件中获取的大型更新查询,假设我有一个包含 (id, banana, apple)
的表。
我想运行一个更新来更改 Bananas 而不是 Apples,每个新 Banana 及其 ID 都将保存在 CSV 文件中。
我尝试查看 Postgres 网站,但这些例子让我很生气。
【问题讨论】:
您不会尝试从 pgadmin3 中执行此操作,是吗?您可能需要某种脚本语言(例如 Python,...)您还需要澄清“更新”的含义。我的疯狂猜测是,您的 CSV 文件包含可能在数据库中也可能不在数据库中的项目,您必须插入或更新它们 - 仅当它们是香蕉时。但是,请澄清。 【参考方案1】:COPY
将文件复制到临时登台表并从那里更新实际表。喜欢:
CREATE TEMP TABLE tmp_x (id int, apple text, banana text); -- but see below
COPY tmp_x FROM '/absolute/path/to/file' (FORMAT csv);
UPDATE tbl
SET banana = tmp_x.banana
FROM tmp_x
WHERE tbl.id = tmp_x.id;
DROP TABLE tmp_x; -- else it is dropped at end of session automatically
如果导入的表与要更新的表完全匹配,这可能很方便:
CREATE TEMP TABLE tmp_x AS SELECT * FROM tbl LIMIT 0;
创建一个与现有表结构匹配的空临时表,没有约束。
特权
在 Postgres 10 之前,SQL COPY
需要超级用户权限。
在 Postgres 11 或更高版本中,还有一些 predefined roles(以前的“默认角色”)允许它。 The manual:
COPY
命名文件或命令只允许数据库超级用户使用 或被授予角色pg_read_server_files
之一的用户,pg_write_server_files
,或pg_execute_server_program
[...]
psql 元命令\copy
适用于任何数据库角色。 The manual:
执行前端(客户端)复制。这是一个运行 SQL
COPY
命令,而不是服务器读取或写入 指定文件,psql读取或写入文件并路由数据 服务器和本地文件系统之间。这意味着该文件 可访问性和权限是本地用户的,而不是 服务器,并且不需要 SQL 超级用户权限。
临时表的作用域仅限于单个角色的单个session,所以以上必须在同一个psql session中执行:
CREATE TEMP TABLE ...;
\copy tmp_x FROM '/absolute/path/to/file' (FORMAT csv);
UPDATE ...;
如果您在 bash 命令中编写脚本,请确保将其全部包装在 single psql 调用中。喜欢:
echo 'CREATE TEMP TABLE tmp_x ...; \copy tmp_x FROM ...; UPDATE ...;' | psql
通常,您需要元命令\\
来在psql 元命令和psql 中的SQL 命令之间切换,但\copy
是此规则的一个例外。 The manual again:
特殊的解析规则适用于
\copy
元命令。与大多数其他元命令不同,该行的整个剩余部分始终作为\copy
的参数,并且在参数中既不执行变量插值也不执行反引号扩展。
大桌子
如果导入表很大,可能需要为会话临时增加temp_buffers
(会话中的第一件事):
SET temp_buffers = '500MB'; -- example value
为临时表添加索引:
CREATE INDEX tmp_x_id_idx ON tmp_x(id);
并手动运行ANALYZE
,因为自动清理/自动分析不涵盖临时表。
ANALYZE tmp_x;
相关答案:
Best way to delete millions of rows by ID How can I insert common data into a temp table from disparate schemas? How to delete duplicate entries?【讨论】:
是的,不错。当事情有时可以变得如此简单时,我总是倾向于使用巨大的机器。 @user519753:刚学了一个新术语——从我在互联网上看到的“谢谢!”一切顺利。 :)COPY tmp_x FROM '/absolute/path/to/file' (DELIMITER ';', HEADER TRUE, FORMAT CSV)
对我来说效果更好。见 (postgresql.org/docs/9.1/static/sql-copy.html)
@taper:我通常在没有任何参数的情况下运行 COPY。但您可能已经注意到,问题是关于 CSV 的。
在UPDATE
-statement 中将USING
替换为FROM
后,这只对我有用(Postgres 9.3)【参考方案2】:
我遇到了同样的问题。但是在这个解决方案中,我发现了一些困难。由于我不是超级用户,使用 copy 会出错。所以我找到了解决我的问题的替代方案。
我正在使用 postgresql 和 pgadmin4。这是我提供的解决方案。
-
创建一个新表并将 fruits 表复制到新表中。
CREATE TABLE fruits_copy AS TABLE fruits WITH NO DATA;
将 CSV 文件数据导入新表(fruits_copy)。我正在使用 pgadmin4,所以这里是 how to import details。 (可能会有所不同)。
从 fruits_copy 表更新 fruits 表。
UPDATE fruits SETbanana = fruits_copy.banana FROM fruits_copy WHERE fruits.id = fruits_copy.id;
-
之后如果你想删除新表,你可以直接删除它。
DROP TABLE fruits_copy;
【讨论】:
【参考方案3】:您可以尝试以下用python编写的代码,输入文件是您要更新到表中的内容的csv文件。每一行都基于逗号分割,因此对于每一行,row[0] 是第一列下的值,row[1] 是第二列下的值,依此类推。
import csv
import xlrd
import os
import psycopg2
import django
from yourapp import settings
django.setup()
from yourapp import models
try:
conn = psycopg2.connect("host=localhost dbname=prodmealsdb
user=postgres password=blank")
cur = conn.cursor()
filepath = '/path/to/your/data_to_be_updated.csv'
ext = os.path.splitext(filepath)[-1].lower()
if (ext == '.csv'):
with open(filepath) as csvfile:
next(csvfile)
readCSV = csv.reader(csvfile, delimiter=',')
for row in readCSV:
print(row[3],row[5])
cur.execute("UPDATE your_table SET column_to_be_updated = %s where
id = %s", (row[5], row[3]))
conn.commit()
conn.close()
cur.close()
except (Exception, psycopg2.DatabaseError) as error:
print(error)
finally:
if conn is not None:
conn.close()
【讨论】:
以上是关于如何使用 Postgres 中 CSV 文件中的值更新选定的行?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 CSV 文件中的标题从 CSV 文件复制到 PostgreSQL 表?
我如何将Postgres DDL转储到可粘贴到Google表格中的CSV中?