【问题标题】:How to update a Postgres table column using a pandas data frame?如何使用 pandas 数据框更新 Postgres 表列?
【发布时间】:2019-03-07 20:35:01
【问题描述】:

我通过 Django(新迁移)将单个列添加到具有 100 多列的 Postgres 表中。如何使用pandas data_frame 中的数据更新 PostgreSQL 表中的列? Postgres SQL UPDATE伪代码是:

UPDATE wide_table wt
SET wt.z = df.z
WHERE date = 'todays_date'

这样做的原因是我正在使用S3 中的CSV 计算data_frame 中的列(这是df.z)。 Postgres update 的文档使用起来很简单,但我不确定如何通过 Django、sqlalchemy、pyodbc 等来执行此操作。

如果这有点令人费解,我深表歉意。一个小而不完整的例子是:

宽表(更新前列z

identifier    |      x       |      y      |      z       |      date       
foo           |      2       |      1      |     0.0      |      ...           
bar           |      2       |      8      |     0.0      |      ...      
baz           |      3       |      7      |     0.0      |      ...      
foo           |      2       |      8      |     0.0      |      ...      
foo           |      1       |      5      |     0.0      |      ...      
baz           |      2       |      8      |     0.0      |      ...      
bar           |      9       |      3      |     0.0      |      ...      
baz           |      2       |      3      |     0.0      |      ...      

Python sn-p 示例

def apply_function(identifier):
    # Maps baz-> 15.0, bar-> 19.6, foo -> 10.0 for single date
    df = pd.read_csv("s3_file_path/date_file_name.csv")
    # Compute 'z' based on identifier and S3 csv
    return z

postgres_query = "Select identifier from wide_table"
df = pd.read_sql(sql=postgres_query, con=engine)
df['z'] = df.identifier.apply(apply_function)

# Python / SQL Update Logic here to update Postgres Column
???

宽表(更新后列z

identifier    |      x       |      y      |      z        |      date 
foo           |      2       |      1      |     10.0      |      ...     
bar           |      2       |      8      |     19.6      |      ... 
baz           |      3       |      7      |     15.0      |      ... 
foo           |      2       |      8      |     10.0      |      ... 
foo           |      1       |      5      |     10.0      |      ... 
baz           |      2       |      8      |     15.0      |      ... 
bar           |      9       |      3      |     19.6      |      ... 
baz           |      2       |      3      |     15.0      |      ... 

注意:z 中的值每天都会变化,因此简单地创建另一个表来保存这些 z 值并不是一个很好的解决方案。另外,我真的希望避免删除所有数据并重新添加。

【问题讨论】:

    标签: python pandas postgresql dataframe


    【解决方案1】:

    遇到了类似的问题,当前接受的解决方案对我来说太慢了。我的表有 500k+ 行,我需要更新 100k+ 行。经过长时间的研究和反复试验,我得出了一个有效且正确的解决方案。

    这个想法是使用 psycopg 作为您的编写器并使用临时表。 df 是您的 pandas 数据框,其中包含您要设置的值。

    import psycopg2
    
    conn = psycopg2.connect("dbname='db' user='user' host='localhost' password='test'")
    cur = conn.cursor()
    
    rows = zip(df.id, df.z)
    cur.execute("""CREATE TEMP TABLE codelist(id INTEGER, z INTEGER) ON COMMIT DROP""")
    cur.executemany("""INSERT INTO codelist (id, z) VALUES(%s, %s)""", rows)
    
    cur.execute("""
        UPDATE table_name
        SET z = codelist.z
        FROM codelist
        WHERE codelist.id = vehicle.id;
        """)
    
    cur.rowcount
    conn.commit()
    cur.close()
    conn.close()
    

    【讨论】:

    • 不错的解决方案!你知道为什么这比我上面的解决方案更快吗?
    • 一方面,在您的解决方案中,您正在触摸未在数据准备过程中更新的行。就我而言,这是很多行。可能这里更大的优化是依靠 SQL 来构建要设置的数据,而不是使用 python。此外,您的解决方案在每次 fn 调用时都会继续读取 csv。
    • 啊,谢谢。是的,函数中的read_csv 肯定是一个非常愚蠢的错误。如果将来我再次需要,我一定会尝试您的解决方案。
    • 对于 1M+ 的大量行,这是否很快?如果我使用 executemany,我发现 executemany 每行执行 1 次更新,我认为这对我来说真的很慢。谢谢。
    【解决方案2】:

    我自己拼凑了一个解决方案,我压缩了 idz 值,然后执行通用 SQL UPDATE 语句并利用 SQL UPDATE FROM VALUES

    数据准备

    sql_query= "SELECT id, a FROM wide_table"
    df = pd.read_sql(sql=sql_query, con=engine)
    df['z'] = df.a.apply(apply_function)
    
    zipped_vals = zip(df.id, df.z)
    tuple_to_str= str(tuple(zipped_vals))
    entries_to_update = tuple_to_str[1:len(tuple_to_str)-1] # remove first and last paren in tuple
    

    SQL查询解决方案:

    # Update column z by matching ID from SQL Table & Pandas DataFrame
    update_sql_query = f"""UPDATE wide_table t SET z = v.z
                            FROM (VALUES {entries_to_update}) AS v (id, z)
                            WHERE t.id = v.id;"""
    
    with engine.begin() as conn:
        conn.execute(update_sql_query)
    
    conn.exec(sql_query)
    

    Answer 关于从值更新 PostgreSQL 表列

    PostgreSQL update docs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-18
      • 1970-01-01
      • 2018-07-08
      • 1970-01-01
      • 1970-01-01
      • 2016-04-02
      • 1970-01-01
      • 2018-08-04
      相关资源
      最近更新 更多