繁体   English   中英

用 pandas DataFrame 替换 mysql 数据库表中的行

[英]REPLACE rows in mysql database table with pandas DataFrame

Python 版本 - 2.7.6

熊猫版 - 0.17.1

MySQLdb 版本 - 1.2.5

在我的数据库 ( PRODUCT ) 中,我有一个表 ( XML_FEED )。 表 XML_FEED 很大(数百万条记录)我有一个 pandas.DataFrame() ( PROCESSED_DF )。 数据框有数千行。

现在我需要运行这个

REPLACE INTO TABLE PRODUCT.XML_FEED
(COL1, COL2, COL3, COL4, COL5),
VALUES (PROCESSED_DF.values)

题:-

有没有办法在熊猫中运行REPLACE INTO TABLE 我已经检查pandas.DataFrame.to_sql()但这不是我需要的。 我不喜欢阅读 pandas 中的XML_FEED表,因为它非常大。

随着 pandas 0.24.0 的发布,现在有一种官方方法可以通过将自定义插入方法传递给to_sql函数来实现这一点。

通过将此可调用对象传递给to_sql ,我能够实现REPLACE INTO的行为:

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)

你会像这样传递它:

df.to_sql(db, if_exists='append', method=mysql_replace_into)

或者,如果你想要INSERT... ON DUPLICATE KEY UPDATE...的行为,你可以使用这个:

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)

编译方法归功于https://stackoverflow.com/a/11762400/1919794

直到这个版本(0.17.1)我无法在熊猫中找到任何直接的方法来做到这一点。 我报告了一个相同的功能请求。 我在我的项目中使用MySQLdb执行一些查询然后使用DataFrame.to_sql(if_exists='append')

认为

1) product_id 是我在表 PRODUCT 中的主键

2) feed_id 是我在表 XML_FEED 中的主键。

简易版

import MySQLdb
import sqlalchemy
import pandas

con = MySQLdb.connect('localhost','root','my_password', 'database_name')
con_str = 'mysql+mysqldb://root:my_password@localhost/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

请注意:- REPLACE [INTO]语法允许我们将一行INSERT表中,除非发生UNIQUE KEY (包括PRIMARY KEY )违规,旧行在新 INSERT 之前被删除,因此没有违规。

我需要一个通用的解决方案来解决这个问题,所以我以 shiva 的答案为基础——也许它会对其他人有所帮助。 这在您从 MySQL 数据库(整个或过滤的)中获取一个表,更新/添加一些行,并希望使用df.to_sql()执行REPLACE INTO语句的情况下很有用。

它找到表的主键,使用 pandas 数据帧中的所有键对 MySQL 表执行删除语句,然后将数据帧插入 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)

如果你使用 to_sql 你应该能够定义它,以便你替换存在的值,所以对于名为“mydb”的表和名为“df”的数据框,你将使用:

df.to_sql(mydb,if_exists='replace')

如果它们已经存在,那应该替换值,但我不能 100% 确定这是否是您要查找的内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM