Esta es el arreglo más acertada que encomtrarás dar, pero mírala pausadamente y analiza si se puede adaptar a tu trabajo.
Solución:
Con el lanzamiento de pandas 0.24.0, ahora hay una forma oficial de lograr esto al pasar un método de inserción personalizado al to_sql
función.
Pude lograr el comportamiento de REPLACE INTO
pasando este invocable a to_sql
:
def mysql_replace_into(table, conn, keys, data_iter):
from sqlalchemy.dialects.mysql import insert
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Insert
@compiles(Insert)
def replace_string(insert, compiler, **kw):
s = compiler.visit_insert(insert, **kw)
s = s.replace("INSERT INTO", "REPLACE INTO")
return s
data = [dict(zip(keys, row)) for row in data_iter]
conn.execute(table.table.insert(replace_string=""), data)
Lo pasarías así:
df.to_sql(db, if_exists='append', method=mysql_replace_into)
Alternativamente, si desea que el comportamiento de INSERT ... ON DUPLICATE KEY UPDATE ...
en su lugar, puedes usar esto:
def mysql_replace_into(table, conn, keys, data_iter):
from sqlalchemy.dialects.mysql import insert
data = [dict(zip(keys, row)) for row in data_iter]
stmt = insert(table.table).values(data)
update_stmt = stmt.on_duplicate_key_update(**dict(zip(stmt.inserted.keys(),
stmt.inserted.values())))
conn.execute(update_stmt)
Créditos a https://stackoverflow.com/a/11762400/1919794 por el método de compilación.
Hasta esta versión (0.17.1)
No puedo encontrar ninguna forma directa de hacer esto en pandas. Informé una solicitud de función para el mismo. Hice esto en mi proyecto con la ejecución de algunas consultas usando MySQLdb
y luego usando DataFrame.to_sql(if_exists='append')
Suponer
1) product_id es mi principal key en la tabla PRODUCTO
2) feed_id es mi principal key en la tabla XML_FEED.
VERSIÓN SIMPLE
import MySQLdb
import sqlalchemy
import pandas
con = MySQLdb.connect('localhost','root','my_password', 'database_name')
con_str = 'mysql+mysqldb://root:[email protected]/database_name'
engine = sqlalchemy.create_engine(con_str) #because I am using mysql
df = pandas.read_sql('SELECT * from PRODUCT', con=engine)
df_product_id = df['product_id']
product_id_str = (str(list(df_product_id.values))).strip('[]')
delete_str = 'DELETE FROM XML_FEED WHERE feed_id IN (0)'.format(product_id_str)
cur = con.cursor()
cur.execute(delete_str)
con.commit()
df.to_sql('XML_FEED', if_exists='append', con=engine)# you can use flavor='mysql' if you do not want to create sqlalchemy engine but it is depreciated
Tenga en cuenta: – El REPLACE [INTO]
La sintaxis nos permite INSERT
una fila en una tabla, excepto que si un UNIQUE KEY
(incluso PRIMARY KEY
) se produce una infracción, la fila anterior se elimina antes que la nueva INSERT, por lo tanto, no hay infracción.
Necesitaba una solución genérica para este problema, así que me basé en la respuesta de Shiva; tal vez sea útil para otros. Esto es útil en situaciones en las que toma una tabla de una base de datos MySQL (total o filtrada), actualiza/agrega algunas filas y desea realizar una REPLACE INTO
declaración con df.to_sql()
.
Encuentra el primario de la tabla. keysrealiza una declaración de eliminación en la tabla MySQL con todos keys desde el marco de datos de pandas, y luego inserta el marco de datos en la tabla MySQL.
def to_sql_update(df, engine, schema, table):
df.reset_index(inplace=True)
sql = ''' SELECT column_name from information_schema.columns
WHERE table_schema = 'schema' AND table_name = 'table' AND
COLUMN_KEY = 'PRI';
'''.format(schema=schema, table=table)
id_cols = [x[0] for x in engine.execute(sql).fetchall()]
id_vals = [df[col_name].tolist() for col_name in id_cols]
sql = ''' DELETE FROM schema.table WHERE 0 '''.format(schema=schema, table=table)
for row in zip(*id_vals):
sql_row = ' AND '.join([''' ='' '''.format(n, v) for n, v in zip(id_cols, row)])
sql += ' OR () '.format(sql_row)
engine.execute(sql)
df.to_sql(table, engine, schema=schema, if_exists='append', index=False)