使用psycopg2将不同长度的行插入Postgres

问题描述 投票:0回答:2

我正在for循环中构建多个不同的pandas数据帧,这些数据帧具有不同的列数,具体取决于我正在抓取的网站可用的数据。

我遇到的问题是当我在初始循环结束时循环数据帧的行以使用psycopg2将它们插入到postgres中时,每个循环的列名长度和行数都会发生变化,这意味着我需要一个动态查询。一定数量的列将始终存在并且具有类型字符,并且可能/可能不存在的列都是数字类型。

这是我已经尝试过的:

con = pypg.connect(user = pg_user, password = pg_pass,
                   host = "pg_host", database = "db",
                   port = "5432")

cursor = con.cursor()

# dt = pandas dataframe with n columns
cols = [i for i in dt.columns if i not in ["column1","column2","column3"]] 

# these columns are always in dt, want to convert others to numeric

for col in cols:
    dt[col]=pd.to_numeric(dt[col])

# Build the string insertion vectors for the correct number of columns
col_insert = "%s, %s, %s,"
data_insert = "%s, %s, %s,"

sql_colnames = tuple(dt.columns)

for i in range(1, (len(sql_colnames) - 2), 1):
  if i != (len(sql_colnames) - 3):
    data_insert = data_insert + " %d,"
    col_insert = col_insert + " %s,"
  elif i == (len(sql_colnames) - 3):
       data_insert = data_insert + " %d"
       col_insert = col_insert + " %s"

# Iterate through the rows of the dataframe and insert them into postgres
for index, row in all_odds_dt.iterrows():
    row_ = tuple(row)
    qry_data = sql_colnames + row_prices
    qry = "INSERT INTO odds_portal_prices (" + col_insert + ") VALUES(" + data_insert + ")" % qry_data

cursor.execute(qry)

我尝试运行查询时收到的错误是

  File "<ipython-input-351-14d7e958b2a7>", line 4, in <module>
    qry = "INSERT INTO odds_portal_prices (" + col_insert + ") VALUES(" + data_insert + ")" % qry_data
TypeError: not all arguments converted during string formatting

我检查了qry_data矢量的长度,以确保它与col_insertdata_insert组合中的元素数量相匹配。

在此先感谢您的帮助。

python python-3.x pandas postgresql psycopg2
2个回答
0
投票

通过参数化,您可以简化大部分处理,而无需担心字符串和数字类型之间的值的字符串格式。但是,首选的str.format用于构建预准备语句,但只能在任何循环之外。

注意:psycopg2的参数占位符是%s,不要与%s%d的Python字符串格式化符号混淆。

### CONVERT NUMERIC COLUMNS WITH apply()
num_cols = dt.columns.difference(["column1","column2","column3"]).values
dt[num_cols] = dt[num_cols].apply(pd.to_numeric)

### BUILD PREPARED STATEMENT (NO DATA)
sql = ("INSERT INTO dbo.Employee_Photo ({sql_cols}) VALUES ({placeholders})"
         .format(sql_cols = ", ".join([i for i in dt.columns]), 
                 placeholders = ", ".join(["%s" for i in dt.columns]))
      )

# EXECUTE PARAMETERIZED QUERY BINDING DF VALUES
cursor.executemany(sql, dt.values.tolist())   
con.commit()

0
投票

您的文字和直接问题与格式化发生的行的分解方式有关。如果我将它扩展为使用一些临时变量,它实际上是这样的:

qry1 = "INSERT INTO odds_portal_prices ("
qry2 = ") VALUES("
qry3 = ")" % qry_data
qry = qry1 + col_insert + qry2 + data_insert + qry3

由于字符串")"中没有格式化点,因此不使用所有格式化参数。

但是,这不是动态构造SQL语句的最佳方法。我建议首先使用值中的列分隔构建语句。然后使用vars参数到光标的execute函数,以安全地将参数输入查询。有关详细信息,请参阅the related psycopg documentation

© www.soinside.com 2019 - 2024. All rights reserved.