使用SQLAlchemy Postgres进行批量Upsert

问题描述 投票:2回答:1

我正在按照SQLAlchemy文档here与Postgres编写批量upsert语句。出于演示目的,我有一个简单的表MyTable

class MyTable(base):
    __tablename__ = 'mytable'
    id = Column(types.Integer, primary_key=True)
    test_value = Column(types.Text)

创建通用插入语句很简单:

from sqlalchemy.dialects import postgresql

values = [{'id': 0, 'test_value': 'a'}, {'id': 1, 'test_value': 'b'}]
insert_stmt = postgresql.insert(MyTable.__table__).values(values)

我遇到的问题是当我尝试添加upsert的“on conflict”部分时。

update_stmt = insert_stmt.on_conflict_do_update(
    index_elements=[MyTable.id],
    set_=dict(data=values)
)

试图执行此语句会产生一个ProgrammingError

from sqlalchemy import create_engine
engine = create_engine('postgres://localhost/db_name')

engine.execute(update_stmt)

>>> ProgrammingError: (psycopg2.ProgrammingError) can't adapt type 'dict'

我认为我的误解在于使用on_conflict_do_update方法构造语句。有谁知道如何构建这个陈述?我已经查看了StackOverflow上的其他问题(例如here),但我似乎无法解决上述错误。

python postgresql sqlalchemy upsert
1个回答
4
投票
update_stmt = insert_stmt.on_conflict_do_update(
    index_elements=[MyTable.id],
    set_=dict(data=values)
)

index_elements应该是字符串列表或列对象列表。所以要么[MyTable.id]['id'](这是正确的)

set_应该是一个字典,列名作为键,有效的sql更新对象作为值。您可以使用excluded属性引用插入块中的值。所以为了得到你想要的结果,你会想要set_={'test_value': insert_stmt.excluded.test_value}(你所做的错误是示例中的data=不是一个神奇的参数......它是示例表中列的名称)

所以,整件事情都会如此

update_stmt = insert_stmt.on_conflict_do_update(
    index_elements=[MyTable.id],
    set_={'test_value': insert_stmt.excluded.test_value}
)

当然,在现实世界的例子中,我通常想要更改一列。在那种情况下,我会做...

update_columns = {col.name: col for col in insert_stmt.excluded if col.name not in ('id', 'datetime_created')}
update_statement = insert_stmt.on_conflict_do_update(index_elements=['id'], set_=update_columns)

(此示例将覆盖除id和datetime_created列之外的每个列)

© www.soinside.com 2019 - 2024. All rights reserved.