如何使用Pandas DataFrame执行db表的现有行的UPDATE?

时间:2017-02-25 21:45:05

标签: python pandas

我正在尝试查询MySql数据库表的子集,将结果提供给Pandas DataFrame,更改一些数据,然后将更新的行写回同一个表。我的表大小是~1MM行,并且我将要改变的行数将相对较小(<50,000),因此返回整个表并执行df.to_sql(tablename,engine, if_exists='replace')不是一个可行的选择。是否有一种直接的方法来更新已更改的行而不迭代DataFrame中的每一行?

我知道这个项目试图模拟“upsert”工作流程,但它似乎只完成了插入新的非重复行而不是更新现有行的部分的任务:

GitHub Pandas-to_sql-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

现在我想将df_in_db写回'test_upsert'表,并反映更新的数据。

这个问题非常相似,其中一条评论建议使用“sqlalchemy表类”来执行任务。

Update table using sqlalchemy table class

如果这是实现它的最佳(唯一?)方式,那么任何人都可以扩展我将如何实现上述特定情况吗?

3 个答案:

答案 0 :(得分:4)

我认为最简单的方法是:

首先删除将要&#34; upserted&#34;的那些行。这可以在循环中完成,但对于更大的数据集(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

PS我没有测试这段代码,所以它可能有一些小错误,但它应该给你一个想法......

答案 1 :(得分:1)

使用Panda's 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)

答案 2 :(得分:0)

我之前一直在为此苦苦挣扎,现在我找到了一种方法。

基本上创建一个单独的数据框,在其中保存您只需要更新的数据。

df #更新数据框中的数据

s_update = "" #更新字符串

遍历数据框。

for i in range(len(df)):
    s_update += "update your_table_name set column_name = '%s' where column_name = '%s';"%(df[col_name1][i], df[col_name2][i])

现在将 s_update 传递给 cursor.execute 或 engine.execute(无论你在哪里执行 SQL 查询)

这会立即更新您的数据。