【问题标题】:Efficiently update rows of a postgres table from another table in another database based on a condition in a common column根据公共列中的条件有效地从另一个数据库中的另一个表中更新 postgres 表的行
【发布时间】:2021-03-13 19:33:39
【问题描述】:

我有两个pandasDataFrames

df1 来自数据库 A,连接参数为 {"host":"hostname_a","port": "5432", "dbname":"database_a", "user": "user_a", "password": "secret_a"}key 列是主键。

df1:

|    | key   | create_date  | update_date   |
|---:|------:|:-------------|:--------------|
|  0 | 57247 | 1976-07-29   | 2018-01-21    | 
|  1 | 57248 |              | 2018-01-21    | 
|  2 | 57249 | 1992-12-22   | 2016-01-31    | 
|  3 | 57250 |              | 2015-01-21    | 
|  4 | 57251 | 1991-12-23   | 2015-01-21    | 
|  5 | 57262 |              | 2015-01-21    | 
|  6 | 57263 |              | 2014-01-21    | 

df2 来自数据库 B,连接参数为 {"host": "hostname_b","port": "5433", "dbname":"database_b", "user": "user_b", "password": "secret_b"}id 列是主键(这些值最初与df1key 列中的值相同;只是对df1 主键列的重命名)。

df2:

|    | id    | create_date  | update_date   | user  |
|---:|------:|:-------------|:--------------|:------|
|  0 | 57247 | 1976-07-29   | 2018-01-21    |       |
|  1 | 57248 |              | 2018-01-21    |       |
|  2 | 57249 | 1992-12-24   | 2020-10-11    | klm   |
|  3 | 57250 | 2001-07-14   | 2019-21-11    | ptl   |
|  4 | 57251 | 1991-12-23   | 2015-01-21    |       |
|  5 | 57262 |              | 2015-01-21    |       |
|  6 | 57263 |              | 2014-01-21    |       |

请注意,df2 中的 row[2] 和 row[3] 的 update_date 值(分别为 2020-10-112019-21-11)比 df1 中的对应值(其中 id = @ 987654349@) 因为他们的creation_date 已被修改(由给定的用户)。

我想更新 df1 的行(即具体而言;create_dateupdate_date 值),其中 df2 中的 update_datedf1 中的原始值更新(对于相同的主键)。

这就是我目前解决这个问题的方法,使用sqlalchemypsycopg2 + .to_sql() 方法pandas'DataFrame

import psycopg2
from sqlalchemy import create_engine
connector = psycopg2.connect(**database_parameters_dictionary)
engine = create_engine('postgresql+psycopg2://', creator=connector)
df1.update(df2) # 1) maybe there is something better to do here?
with engine.connect() as connection:
    df1.to_sql(
        name="database_table_name",
        con=connection,
        schema="public",
        if_exists="replace", # 2) maybe there is also something better to do here?
        index=True
    )

我的问题是,根据文档,if_exists 参数只能做三件事:

if_exists{‘fail’, ‘replace’, ‘append’},默认‘fail’

因此,要更新这两行,我必须;
1) 在df1 上使用.update() 方法,使用df2 作为参数,以及
2) 替换.to_sql() 方法中的整个表,意思是“drop+recreate”。
由于表格非常大(超过 500'000 个条目),我觉得这需要很多不必要的工作!

我怎样才能有效地只更新这两个新更新的行?我是否必须生成一些自定义 SQL 查询来比较每一行的日期,并且只取那些真正改变的日期?但是在这里,我有直觉,循环遍历所有行以比较更新日期将花费“很多”时间。如何更有效地做到这一点? (如果两个表在同一个主机/数据库上,在纯 SQL 中会更容易,但不幸的是,情况并非如此)。

【问题讨论】:

    标签: python-3.x pandas postgresql dataframe sqlalchemy


    【解决方案1】:

    Pandas 不能对表格进行部分更新,不。 .to_sql() 中有一个 longstanding open bug 用于支持子整表粒度更新,但您可以从那里的讨论中看到,在一般情况下支持它是一个非常复杂的功能。

    但是,仅限于您的情况,我认为您可以采取合理的方法。

    不使用df1.update(df2),而是组合一个表达式,该表达式只产生带有新值的更改记录(我不经常使用pandas,所以我不知道这个);然后迭代生成的数据框并自己构建 UPDATE 语句(或使用 SQLAlchemy 表达式层,如果您正在使用它)。然后,使用与 DB A 的连接将所有 UPDATE 作为一个事务发出。使用带索引的 PK,它应该与预期的一样快。

    顺便说一句,我认为 df1.update(df2) 并不完全正确 - 从我的阅读来看,这将更新所有具有任何不同字段的行,而不仅仅是在 updated_date > prev updated_date 时。但是,如果 df2 中的 updated_date 仅比 df1 中的更新日期更新,那就没有实际意义了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-17
      • 2022-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多