Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用“插入”从pandas插入postgreSQL表;“关于冲突”;更新_Python_Pandas_Postgresql_Dataframe_Insert Update - Fatal编程技术网

Python 使用“插入”从pandas插入postgreSQL表;“关于冲突”;更新

Python 使用“插入”从pandas插入postgreSQL表;“关于冲突”;更新,python,pandas,postgresql,dataframe,insert-update,Python,Pandas,Postgresql,Dataframe,Insert Update,我有一个熊猫数据框,我需要存储到数据库中。以下是我当前插入的代码行: df.to_sql(table,con=engine,if_exists='append',index_label=index_col) 如果我的表中不存在df中的任何行,则该方法可以正常工作。如果行已存在,则会出现以下错误: sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) duplicate key value violates unique constra

我有一个熊猫数据框,我需要存储到数据库中。以下是我当前插入的代码行:

df.to_sql(table,con=engine,if_exists='append',index_label=index_col)
如果我的表中不存在
df
中的任何行,则该方法可以正常工作。如果行已存在,则会出现以下错误:

sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) duplicate key
value violates unique constraint "mypk"
DETAIL:  Key (id)=(42) already exists.
 [SQL: 'INSERT INTO mytable (id, owner,...) VALUES (%(id)s, %(owner)s,...']
 [parameters:...] (Background on this error at: http://sqlalche.me/e/gkpj)
没有插入任何内容

PostgreSQL具有可选的
ON CONFLICT
子句,可用于
更新现有表行。我阅读了全文,但找不到任何方法在
DataFrame.to_sql()
函数中使用冲突

我考虑过根据db表中已有的内容将数据帧一分为二。现在我有两个数据帧,
insert_rows
update_rows
,我可以安全地执行

insert_rows.to_sql(table, con=engine, if_exists='append', index_label=index_col)

但是,似乎没有
UPDATE
等价于
DataFrame.to\u sql()
。那么,如何使用DataFrame更新表中的行呢?

如果您注意到在
to_sql
文档中提到了一个采用可调用。创建这个callable应该允许您使用所需的Postgres子句。以下是他们在文档中提到的可调用的示例:


它与您需要的非常不同,但是请遵循传递给此callable的参数。它们将允许您构造一个常规的SQL语句。

以一个例子来跟进Brendan的答案,以下是对我有效的方法:

import os
import sqlalchemy as sa
import pandas as pd
from sqlalchemy.dialects.postgresql import insert


engine = sa.create_engine(os.getenv("DBURL"))
meta = sa.MetaData()
meta.bind = engine
meta.reflect(views=True)


def upsert(table, conn, keys, data_iter):
    upsert_args = {"constraint": "test_table_col_a_col_b_key"}
    for data in data_iter:
        data = {k: data[i] for i, k in enumerate(keys)}
        upsert_args["set_"] = data
        insert_stmt = insert(meta.tables[table.name]).values(**data)
        upsert_stmt = insert_stmt.on_conflict_do_update(**upsert_args)
        conn.execute(upsert_stmt)


if __name__ == "__main__":
    df = pd.read_csv("test_data.txt")
    with db.engine.connect() as conn:
        df.to_sql(
            "test_table",
            con=conn,
            if_exists="append",
            method=upsert,
            index=False,
        )
在本例中,模式类似于:

CREATE TABLE test_table(
    col_a text NOT NULL,
    col_b text NOT NULL,
    col_c text,
    UNIQUE (col_a, col_b)
)

如果有人想在
zdgriffith
的答案基础上构建并动态生成表约束名称,您可以使用以下postgreSQL查询:

select distinct tco.constraint_name
from information_schema.table_constraints tco
         join information_schema.key_column_usage kcu
              on kcu.constraint_name = tco.constraint_name
                  and kcu.constraint_schema = tco.constraint_schema
                  and kcu.constraint_name = tco.constraint_name
where kcu.table_name = '{table.name}'
  and constraint_type = 'PRIMARY KEY';
然后可以格式化此字符串以填充
table.name
中的
upsert()
方法


我也不需要
meta.bind
meta.reflect()
行。不管怎样,后者很快就会被弃用。

不幸的是,这在实践中非常缓慢。是否有一种更快的方式升级Pandas/SQL?