如何使用 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 会出错。所以我找到了解决我的问题的替代方案。

我正在使用 postgresqlpgadmin4。这是我提供的解决方案。

    创建一个新表并将 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中?

如何使用 CSVHelper 更新现有 CSV 文件中特定列中的值?

python中,如何将列表中的值,竖着存在csv文件中

在 postgres 上将表导出到 csv

如果列中的值小于特定值,如何转到csv文件中的特定列并打印整行