如何使用Postgres中CSV文件中的值更新所选行?


76

我正在使用Postgres,并想进行一个大型更新查询,该查询将从CSV文件中提取,可以说我有一个表,该表具有(id, banana, apple)

我想运行一个更新来更改香蕉而不是苹果,每个新的香蕉及其ID都将保存在CSV文件中。

我尝试查看Postgres网站,但这些示例使我丧命。


您不是要在pgadmin3中这样做,是吗?您可能需要某种脚本语言(例如Python,...),还需要阐明“更新”的含义。我的疯狂猜测是,您的CSV文件包含数据库中可能存在或可能不存在的项目,并且您必须插入或更新它们-仅当它们是香蕉时。但是,请澄清。

Answers:


162

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;

无约束地创建一个与现有表结构匹配的空临时表。

礼遇

SQLCOPY为此需要超级用户特权。(本手册):

COPY 仅允许数据库超级用户为文件或命令命名,因为它允许读取或写入服务器有权访问的任何文件。

psql的元命令\copy适用于任何数据库的作用。手册:

执行前端(客户端)副本。这是运行SQLCOPY命令的操作,但是psql读取或写入文件并在服务器和本地文件系统之间路由数据,而不是服务器读取或写入指定的文件。这意味着文件可访问性和特权是本地用户(而不是服务器)的文件可访问性和特权,并且不需要SQL超级用户特权。

临时表的范围限于单个角色的单个会话,因此上述操作必须在同一psql会话中执行:

CREATE TEMP TABLE ...;
\copy tmp_x FROM '/absolute/path/to/file' (FORMAT csv);
UPDATE ...;

如果要在bash命令中编写脚本,请确保将其全部包装在单个psql调用中。喜欢:

echo 'CREATE TEMP TABLE tmp_x ...; \copy tmp_x FROM ...; UPDATE ...;' | psql

通常,您需要meta命令\\在psql元命令和psql中的SQL命令之间切换,但这\copy是该规则的例外。再次手册:

特殊的解析规则适用于\copymeta命令。与大多数其他元命令不同,该行的其余全部始终作为的参数\copy,并且在参数中既不执行变量插值也不执行反引号扩展。

大桌子

如果导入表很大,则可能temp_buffers需要为该会话临时增加(会话中的第一件事):

SET temp_buffers = '500MB';  -- example value

向临时表添加索引:

CREATE INDEX tmp_x_id_idx ON tmp_x(id);

ANALYZE手动运行,因为临时表未包含在autovacuum / auto-analyze中。

ANALYZE tmp_x;

相关答案:


是的,很好。当事情有时可以变得如此简单时,我总是倾向于庞大的机器。

@ user519753:刚学了一个新名词-从互联网上我看到的是“谢谢!” 一切顺利。:)
Erwin Brandstetter,2012年

4
COPY tmp_x FROM '/absolute/path/to/file' (DELIMITER ';', HEADER TRUE, FORMAT CSV)为我工作得更好。见(postgresql.org/docs/9.1/static/sql-copy.html
锥度

1
@taper:我通常在不带任何参数的情况下运行COPY。但是您可能已经注意到,问题是关于CSV的
Erwin Brandstetter

1
这只能更换后的工作对我来说(Postgres的9.3)USINGFROMUPDATE语句来
ARTM

-1

您可以尝试下面用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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.