我试图查询MySql数据库表的子集,将结果提供给Pandas DataFrame,更改一些数据,然后将更新的行写回同一个表.我的表大小约为1MM行,我将要改变的行数相对较小(<50,000),因此返回整个表并执行a df.to_sql(tablename,engine, if_exists='replace')不是一个可行的选择.是否有一种直接的方法来更新已更改的行而不迭代DataFrame中的每一行?
我知道这个项目,它试图模拟"upsert"工作流程,但它似乎只完成了插入新的非重复行而不是更新现有行的部分的任务:
以下是我试图以更大规模完成的内容的框架:
import pandas as pd
from sqlalchemy import create_engine
import threading
#Get sample data
d = {'A' : [1, 2, 3, 4], 'B' : [4, 3, 2, 1]}
df = pd.DataFrame(d)
engine = create_engine(SQLALCHEMY_DATABASE_URI)
#Create a table with a unique constraint on A.
engine.execute("""DROP TABLE IF EXISTS test_upsert """)
engine.execute("""CREATE TABLE test_upsert (
A INTEGER,
B INTEGER,
PRIMARY KEY (A))
""")
#Insert data using pandas.to_sql
df.to_sql('test_upsert', engine, if_exists='append', index=False)
#Alter row where 'A' == 2
df_in_db.loc[df_in_db['A'] == 2, 'B'] = 6
Run Code Online (Sandbox Code Playgroud)
现在我想df_in_db回到我的'test_upsert'表格,反映更新的数据.
这个SO问题非常相似,其中一条评论建议使用"sqlalchemy表类"来执行任务.
如果这是实现它的最佳(唯一?)方式,任何人都可以扩展我将如何实现上述特定情况吗?
我认为最简单的方法是:
首先删除将要“插入”的那些行。这可以循环执行,但对于较大的数据集(5K +行)而言效率不高,因此我将DF的这一部分保存到临时MySQL表中:
# assuming we have already changed values in the rows and saved those changed rows in a separate DF: `x`
x = df[mask] # `mask` should help us to find changed rows...
# make sure `x` DF has a Primary Key column as index
x = x.set_index('a')
# dump a slice with changed rows to temporary MySQL table
x.to_sql('my_tmp', engine, if_exists='replace', index=True)
conn = engine.connect()
trans = conn.begin()
try:
# delete those rows that we are going to "upsert"
engine.execute('delete from test_upsert where a in (select a from my_tmp)')
trans.commit()
# insert changed rows
x.to_sql('test_upsert', engine, if_exists='append', index=True)
except:
trans.rollback()
raise
Run Code Online (Sandbox Code Playgroud)
PS我没有测试此代码,因此它可能有一些小错误,但它应该给您一个想法...
使用Panda 的 to_sql “方法”arg 和 sqlalchemy 的mysql insert on_duplicate_key_update功能的MySQL 特定解决方案:
def create_method(meta):
def method(table, conn, keys, data_iter):
sql_table = db.Table(table.name, meta, autoload=True)
insert_stmt = db.dialects.mysql.insert(sql_table).values([dict(zip(keys, data)) for data in data_iter])
upsert_stmt = insert_stmt.on_duplicate_key_update({x.name: x for x in insert_stmt.inserted})
conn.execute(upsert_stmt)
return method
engine = db.create_engine(...)
conn = engine.connect()
with conn.begin():
meta = db.MetaData(conn)
method = create_method(meta)
df.to_sql(table_name, conn, if_exists='append', method=method)
Run Code Online (Sandbox Code Playgroud)