我正在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_insert
和data_insert
组合中的元素数量相匹配。
在此先感谢您的帮助。
通过参数化,您可以简化大部分处理,而无需担心字符串和数字类型之间的值的字符串格式。但是,首选的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()
您的文字和直接问题与格式化发生的行的分解方式有关。如果我将它扩展为使用一些临时变量,它实际上是这样的:
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。