【问题标题】:psycopg2.copy_from: Remove quotes in text when importing from CSVpsycopg2.copy_from:从 CSV 导入时删除文本中的引号
【发布时间】:2016-02-23 21:22:25
【问题描述】:

我有一个 CSV 文件,其中包含引用的所有条目,即带有开始和结束引号。当我使用copy_from 导入数据库时​​,数据库表包含数据的引号,并且在有空条目的地方我只得到引号,即列中的“”条目,如下所示

[

有没有办法告诉 copy_from 忽略引号,以便在我导入文件时文本周围没有引号,并且空条目将转换为 Null,如下所示?

这是我的代码:

with open(source_file_path) as inf:
  cursor.copy_from(inf, table_name, columns=column_list, sep=',', null="None")

更新

我仍然没有解决上述问题,但是为了导入文件,我继续编写原始 SQL 代码并在 SQLAlchemy 连接和 Pyscopg2 的光标中执行它,如下所示,他们都删除了引号并放入有空条目的地方为 Null。

sql = "COPY table_name (col1, col2, col3, col4) FROM '{}' DELIMITER ',' CSV HEADER".format(csv_file_path)

SQL 炼金术:

conn = engine.connect()
trans = conn.begin()
conn.execute(sql)
trans.commit()
conn.close()

Psycopg2:

conn = psycopg2.connect(pg_conn_string)
conn.set_isolation_level(0)
cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
cursor = conn.cursor()
cursor.execute(sql)

虽然仍然希望 copy_from 函数能够正常工作,但现在我想知道上述两个函数是否与 copy_from 一样快,如果是,两者哪个更快?

【问题讨论】:

    标签: sqlalchemy psycopg2


    【解决方案1】:

    可能更好的方法是使用内置的 CSV 库来读取 CSV 文件并将行传输到数据库。 UNIX 哲学“做一件事并把它做好”的必然结果是使用适当的工具(专门的工具)来完成这项工作。 CSV 库的优点在于您可以自定义选项来读取 CSV,例如引用字符和跳过初始行(请参阅 documentation)。

    假设一个简单的 CSV 文件有两列:一个整数“ID”和一个带引号的字符串“国家代码”:

    "ID", "Country Code"
    1, "US"
    2, "UK"
    

    和一个声明性 SQLAlchemy 目标表:

    from sqlalchemy import create_engine, Column, Integer, String
    from sqlalchemy.ext.declarative import declarative_base
    
    
    engine = create_engine("postgresql+psycopg2://<REMAINDER_OF_YOUR_ENGINE_STRING>")
    Base = declarative_base(bind=engine)
    
    class CountryTable(Base):
        __tablename__ = 'countries'
    
        id = Column(Integer, primary_key=True)
        country = Column(String)
    

    您可以通过以下方式传输数据:

    import csv
    
    from sqlalchemy.orm import sessionmaker
    
    from your_model_module import engine, CountryTable
    
    
    Session = sessionmaker(bind=engine)
    with open("path_to_your.csv", "rb") as f:
        reader = csv.DictReader(f)
        session = Session()
        for row in reader:
            country_record = CountryTable(id=row["ID"], country=row["Country Code"])
            session.add(country_record)
            session.commit()
        session.close()
    

    此解决方案比单行 .copy_from 方法更长,但它可以让您更好地控制,而无需深入研究代码/理解包装器或便利函数(如 .copy_from)的文档。您可以指定要传输的选定列并在行级别处理异常,因为数据是通过提交逐行传输的。可以通过单个提交批量传输行:

    with open("path_to_your.csv", "rb") as f:
        reader = csv.DictReader(f)
        session = Session()
        session.add_all([
            CountryTable(id=row["ID"], country=row["Country Code"]) for row in reader
            ])
        session.commit()
        session.close()    
    

    要比较解决问题的不同方法的执行时间,请使用 Python 附带的 timeit 模块(或者更确切地说是命令行命令)。但请注意:it's better to be correct than fast.


    编辑:

    我试图找出 .copy_from 的编码位置,因为我以前没有使用过它。原来是a psycopg2 specific convenience function。它不是 100% 支持读取 CSV 文件,但只支持文件类对象。适用于 CSV 的唯一自定义参数是分隔符。它不理解引用字符。

    【讨论】:

    • 谢谢。我广泛使用 CSV 库,但当我需要控制每一列和每一行(单元格)时,我会使用它,尤其是在验证数据时。然而,在这种情况下,数据符合所有要求,并且它位于多个 14GB 文件中,因此当一个简单的copy_from 需要几分钟来吞噬这些类型的文件时,感觉就像逐行读取将是“浪费时间”。
    • 我刚遇到这个问题。多么愚蠢的功能。他们应该把它扔掉。
    猜你喜欢
    • 2022-01-08
    • 1970-01-01
    • 2021-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 2021-07-25
    • 2014-05-05
    相关资源
    最近更新 更多