Reputation: 3
I have the below function for generating an SQL query dynamically to insert a pandas dataframe in postgres using psycopg2. I'll be using this function to insert multiple dataframes which might not have all the columns in the database hence why I'm not using pandas.to_sql().
I keep getting the error ValueError: unsupported format character: '('
and can't figure what is causing it.
Any help would be appreciated.
def execute_values(conn, df, schema, table):
"""
Using psycopg2.extras.execute_values() to insert the dataframe
"""
# Create a tuple of dicts from the dataframe values
dicts = tuple(df.to_dict('records'))
columns = sql.SQL(',').join(map(sql.Identifier, list(df.columns)))
values = sql.SQL(',').join(map(sql.Placeholder, list(df.columns)))
# SQL query to execute
query = sql.SQL('INSERT INTO {} ({}) VALUES ({})').format(
sql.Identifier(schema, table),
columns,
values
)
cursor = conn.cursor()
try:
extras.execute_values(cursor, query, dicts)
conn.commit()
except (Exception, psycopg2.DatabaseError) as error:
print("Error: %s" % error)
conn.rollback()
cursor.close()
raise
print("execute_values() done")
cursor.close()
Upvotes: 0
Views: 1305
Reputation: 19684
How to do make dynamic SQL using execute_values
:
import psycopg2
from psycopg2.extras import execute_values
from psycopg2 import sql
con = psycopg2.connect("dbname=test host=localhost user=aklaver")
cur = con.cursor()
columns = sql.SQL(',').join(map(sql.Identifier, ['id', 'cond', 'animal']))
data_dict = [{"id": 20, "cond": "fair", "animal": "fish"}, {"id": 30, "cond": "poor", "animal": "fish"}, {"id": 40, "cond": "great", "animal": "fish"}]
values = sql.SQL(',').join(map(sql.Placeholder, ['id', 'cond', 'animal']))
# Since you are using a dict you need to provide a template for
# execute_values
values_template = sql.SQL('(') + values + sql.SQL(')')
print(values_template.as_string(con)) (%(id)s,%(cond)s,%(animal)s)
insert_sql = sql.SQL("INSERT INTO {} ({}) VALUES %s").format(sql.Identifier("animals"), columns)
print(insert_sql.as_string(con))
INSERT INTO "animals" ("id","cond","animal") VALUES %s
execute_values(cur, insert_sql, data_dict, template=values_template)
con.commit()
select * from animals ;
id | cond | animal
----+-------+--------
20 | fair | fish
30 | poor | fish
40 | great | fish
Upvotes: 1