【问题标题】:Pandas update sql熊猫更新sql
【发布时间】:2019-07-28 02:36:37
【问题描述】:

有没有什么方法可以在不遍历每一行的情况下从数据框中进行 SQL 更新?我有一个 postgresql 数据库,要从数据框中更新数据库中的表,我会使用 psycopg2 并执行以下操作:

con = psycopg2.connect(database='mydb', user='abc', password='xyz')
cur = con.cursor()

for index, row in df.iterrows():
    sql = 'update table set column = %s where column = %s'
    cur.execute(sql, (row['whatver'], row['something']))
con.commit()

但另一方面,如果我从 sql 读取表或将整个数据帧写入 sql(没有更新位置),那么我只会使用 pandas 和 sqlalchemy。比如:

engine = create_engine('postgresql+psycopg2://user:pswd@mydb')
df.to_sql('table', engine, if_exists='append')

使用 to_sql 有一个“单线”真是太好了。从熊猫到postgresql没有类似的更新吗?或者是通过像我上面所做的那样遍历每一行来做到这一点的唯一方法。遍历每一行不是一种低效的方法吗?

【问题讨论】:

  • 你能用to_sql将数据帧写入临时表,然后在postgresql中进行更新吗?

标签: python postgresql pandas


【解决方案1】:

考虑一个临时表,它是您最终表的精确副本,每次运行都会清除:

engine = create_engine('postgresql+psycopg2://user:pswd@mydb')
df.to_sql('temp_table', engine, if_exists='replace')

sql = """
    UPDATE final_table AS f
    SET col1 = t.col1
    FROM temp_table AS t
    WHERE f.id = t.id
"""

with engine.begin() as conn:     # TRANSACTION
    conn.execute(sql)

【讨论】:

  • 另外,“REPLACE INTO final_table (SELECT * FROM temp_table)”将根据 final_table 的唯一键更新所有列(并插入新列)。
  • Postgres 是否支持REPLACE INTO
  • @cqcn1991,您必须在UPDATE 中为每一列添加final_col = temp_col 子句。这里只显示一列。然后可以一次运行所有操作。
  • 我正在寻求解决同样的问题。当我尝试使用这个 sql 字符串时,我得到 MySQLdb._exceptions.ProgrammingError) (1064,“您的 SQL 语法有错误;请查看与您的 MySQL 服务器版本相对应的手册,以获取在附近使用的正确语法。它看起来如何像 2019 年那样?
  • @GeorgiIvanovDimitrov ...这是一个 Postgres 解决方案。 MySQL 在UPDATE 查询中不使用FROM。它确实使用UPDATE ... JOIN。阅读文档、教程,甚至 SO 帖子。
【解决方案2】:

看起来您正在使用存储在df 中的一些外部数据作为更新数据库表的条件。如果可以,为什么不只做一行 sql 更新?

如果您使用的是小型数据库(将整个数据加载到 python 数据框对象不会杀死您),那么您可以在使用 @ 加载后肯定有条件地更新数据框987654325@。然后可以使用关键字 arg if_exists="replace" 将 DB 表替换为新更新的表。

df = pandas.read_sql("select * from your_table;", engine)

#update information (update your_table set column = "new value" where column = "old value")
#still may need to iterate for many old value/new value pairs
df[df['column'] == "old value", "column"] = "new value"

#send data back to sql
df.to_sql("your_table", engine, if_exists="replace")

Pandas 是一个强大的工具,起初有限的 SQL 支持只是一个小功能。随着时间的推移,人们开始尝试使用 pandas 作为他们唯一的数据库接口软件。我不认为 pandas 本来就打算成为数据库交互的终结者,但是有很多人一直在研究新功能。见:https://github.com/pandas-dev/pandas/issues

【讨论】:

  • 表是否在事务中删除并重新创建?看了to_sql() source code 之后,我认为答案是“不”,但我不确定。我担心的是,有时其他用户可能会尝试查询该表并发现它不存在。
  • 如果 to_sql() 方法的属性设置为“ if_exists='replace' ”,它将删除表。所以在这个例子中,是的。见:pandas.pydata.org/pandas-docs/stable/reference/api/…
  • 这样做的问题是它还必须删除所有依赖视图。
  • if_exists = "replace" 被删除的事实也意味着表的约束和数据库中设置的数据类型会丢失并自动替换,这在大多数情况下是不可取的行为。
【解决方案3】:

到目前为止,我还没有看到可以以任何可扩展的方式使用 pandas sql 连接器来更新数据库数据的情况。构建一个似乎是个好主意,但实际上,对于运营工作,它只是无法扩展。

我建议将整个数据帧转储为 CSV 使用

df.to_csv('filename.csv', encoding='utf-8')

然后使用 COPY 用于 PostgreSQL 或 LOAD DATA INFILE 用于 MySQL 将 CSV 加载到数据库中。

如果在 pandas 处理数据时您没有对相关表进行其他更改,则可以直接加载到表中。

如果存在并发问题,您必须将数据加载到临时表中,然后使用该表来更新主表。

在后一种情况下,您的主表需要有一个日期时间来告诉您对它的最新修改时间,以便您可以确定您的 pandas 更改是最新的还是应该保留数据库更改。

【讨论】:

  • 它当然更具可扩展性,但也更难))因为我们必须考虑 CSV 交付方式)
【解决方案4】:

我想知道你为什么不先根据你的方程更新 df 然后将 df 存储到数据库中,你可以使用 if_exists='replace' 来存储在同一张表上。

【讨论】:

    【解决方案5】:

    如果列名没有改变,我更喜欢删除所有行,然后将数据附加到现在为空的表中。否则,依赖视图也必须重新生成:

    from sqlalchemy import create_engine
    from sqlalchemy import MetaData
    
    engine = create_engine(f'postgresql://postgres:{pw}@localhost:5432/table')   
    
    # Get main table and delete all rows
    # without deleting the table
    meta = MetaData(engine)
    meta.reflect(engine)
    table = meta.tables['table']
    del_st = table.delete()
    
    conn = engine.connect()
    res = conn.execute(del_st)
    
    # Insert new data    
    df.to_sql('table', engine, if_exists='append', index=False)
    

    【讨论】:

      【解决方案6】:

      我尝试了第一个答案,发现效果不太好,然后我更改了一些部分以通过使用 pandas+sqlalchemy 更新的所有情况。

      def update_to_sql(self, table_name, key_name)
          a = []
          self.table = table_name
          self.primary_key = key_name
          for col in df.columns:
              if col == self.primary_key:
                  continue
              a.append("f.{col}=t.{col}".format(col=col))
          df.to_sql('temporary_table', self.sql_engine, if_exists='replace', index=False)
          update_stmt_1 = "UPDATE {final_table} AS f".format(final_table=self.table)
          update_stmt_2 = " INNER JOIN (SELECT * FROM temporary_table) AS t ON t.{primary_key}=f.{primary_key} ".format(primary_key=self.primary_key)
          update_stmt_3 = "SET "
          update_stmt_4 = ", ".join(a)
          update_stmt_5 = update_stmt_1 + update_stmt_2 + update_stmt_3 + update_stmt_4 + ";"
          print(update_stmt_5)
          with self.sql_engine.begin() as cnx:
              cnx.execute(update_stmt_5)
      

      【讨论】:

      • 你能添加关于你的代码做什么的描述吗?
      【解决方案7】:

      这是我发现有些干净的方法。这利用了 sqlalchemy。它一次只更新一列,但很容易泛化。

      def dataframe_update(df, table, engine, primary_key, column):
        md = MetaData(engine)
        table = Table(table, md, autoload=True)
        session = sessionmaker(bind=engine)()
        for _, row in df.iterrows():
          session.query(table).filter(table.columns[primary_key] == row[primary_key]).update({column: row[column]})
        session.commit()
      

      【讨论】:

        猜你喜欢
        • 2021-01-18
        • 1970-01-01
        • 2020-09-19
        • 2017-02-21
        • 2018-12-29
        • 2020-04-27
        • 2021-04-23
        • 1970-01-01
        相关资源
        最近更新 更多