【问题标题】:Inserting data from a CSV file to postgres using SQL使用 SQL 将数据从 CSV 文件插入到 postgres
【发布时间】:2019-12-02 12:34:15
【问题描述】:

由于我是新手,而且我在这门语言方面没有丰富的经验,因此一直在努力解决这个 python 问题。我目前有一个 CSV 文件,其中包含大约 20 个标题和相同数量的行,因此像下面的一些示例一样列出每个是我试图避免的: https://www.dataquest.io/blog/loading-data-into-postgres/

到目前为止,我的代码包含以下内容:

  with open('dummy-data.csv', 'r') as f:
        reader = csv.reader(f)
        next(reader)
        for row in reader:
            cur.execute('INSERT INTO messages VALUES', (row))

我在输入末尾遇到语法错误,所以我认为它与我的执行方法的编写方式有关,但我仍然不知道我会做什么来解决这个问题。有什么帮助吗?

附:我了解使用 %s 的人,但如果是这种情况,是否可以避免,因为我不想让它在一行中重复 20 次。

【问题讨论】:

    标签: python postgresql csv


    【解决方案1】:

    基本上,您必须在查询中指定至少所需的占位符 - 最好也指定字段名称。

    如果这是一次性事件,并且您知道 CSV 中的哪些字段以及顺序,那么您只需在查询中对它们进行硬编码,即

    SQL =  "insert into tablename(field1, field2, field21) values(%s, %s, %s)"
    

    好的,对于 20 个左右的字段,它会变得很无聊,因此您还可以使用字段名称列表来生成字段名称部分和占位符:

    fields = ["field1", "field2", "field21"]
    placeholders = ["%s"] * len(fields) # list multiplication, yes
    
    SQL = "insert into tablename({}) values({})".format(", ".join(fields), ", ".join(placeholders))
    

    如果 CSV 标题行包含确切的字段名称,您也可以将此行用作 fields 的值 - 但您必须信任 csv。

    注意:并非严格要求在查询中指定字段列表,但它可以保护您免受格式错误的 csv 可能出现的问题。实际上,除非您真的信任源(您的 csv),否则您应该在将传入数据发送到数据库之前主动验证它们。

    NB2:

    %s 适用于我知道的字符串,但它对时间戳的作用是否相同?

    在这种情况下,"%s" 不用作 Python 字符串格式说明符,而是用作普通的数据库查询占位符。在这里选择字符串格式说明符真的很不幸,因为它造成了很多混乱。请注意,这是特定于数据库供应商的,但有些供应商使用“?”相反,恕我直言,这更清楚(并且您想检查自己的 db-api 连接器的文档以获取正确的占位符以使用 BTW)。

    而且由于它不是字符串格式说明符,它适用于任何类型并且不需要为字符串引用,这是 db-api 模块的工作,根据 db 列进行正确的格式化(包括引用等)类型。

    尽管我们这样做了,但绝对不要在将值传递给您的查询时直接使用 Python 字符串格式化操作 - unless you want your database to be open-bar for script-kiddies of course

    【讨论】:

    • 关于您的解决方案,%s 适用于我知道的字符串,但它对时间戳的作用是否相同?
    【解决方案2】:

    问题在于插入本身:

     cur.execute('INSERT INTO messages VALUES', (row))
    

    问题是,由于你没有在查询中定义参数,它解释了你真的想执行INSERT INTO messages VALUES,没有参数,这会导致语法错误;使用单个参数也不起作用,因为它会理解您需要单个参数,而不是多个参数。

    如果你想以更动态的方式创建参数,你可以尝试动态构造查询字符串。

    请看一下文档:http://initd.org/psycopg/docs/cursor.html#cursor.execute

    【讨论】:

      【解决方案3】:

      您可以使用字符串相乘。

      import csv
      import psycopg2
      
      conn = psycopg2.connect('postgresql://db_user:db_user_password@server_name:port/db_name')
      cur = conn.cursor()
      
      multiple_placehorders = ','.join(['%s']*20)
      with open('dummy-data.csv', 'r') as f:
          reader = csv.reader(f)
          next(reader)
          for row in reader:
              cur.execute('INSERT INTO public.messages VALUES (' + multiple_placehorders + ')', row)
      
      conn.commit()
      

      【讨论】:

      • 杰斐逊你可能想了解str.join()-它避免了尾随逗号问题。
      【解决方案4】:

      如果您想要一个覆盖整个值列表的占位符,您可以使用位于“extras”中的不同方法,它涵盖了该用法:

      psycopg2.extras.execute_values(cur, 'INSERT INTO messages VALUES %s', (row,))
      

      这种方法一次可以占用多行(这对性能有好处),这就是为什么您需要将单行包装在 (...,) 中。

      【讨论】:

        【解决方案5】:

        上次当我努力将 CSV 数据插入到 postgres 中时,我使用了 pgAdmin,它已经奏效了。我不知道这个答案是否是一个解决方案,但一个容易相处的想法。

        【讨论】:

        • 这是一个非常明智和有价值的评论,但它确实应该是一个评论 - 它没有解释为什么 OP 有这个错误,也没有解释他如何解决它。
        • 我知道,我没有直接评论这个问题的声誉(典型的 StackO)。但我真的很想分享我以前做过的事情。所以。
        【解决方案6】:

        你可以使用游标和executemany,这样你就可以跳过迭代,但它比字符串连接参数化的方法慢。

        import pandas
        df = pd.read_csv('dummy-data.csv')
        df.columns = [<define the headers  here>] # You can skip this line if headers match column names
        try:
            cursor.prepare("insert into public.messages(<Column Names>) values(:1, :2, :3 ,:4, :5)")
            cursor.executemany(None, df.values.tolist())
            conn.commit()
        except:
            conn.rollback()
        

        【讨论】:

          猜你喜欢
          • 2021-04-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-27
          • 2020-04-26
          • 2018-05-10
          • 2016-06-09
          • 1970-01-01
          相关资源
          最近更新 更多