【问题标题】:Bulk Upsert with SQLAlchemy Postgres使用 SQLAlchemy Postgres 批量更新
【发布时间】:2019-08-17 11:40:34
【问题描述】:

我正在按照 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 的“冲突时”部分时。

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】:
    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 列之外的每一列)

    【讨论】:

    • 很好的答案 - 超级清晰且易于理解。谢谢!
    • 这将我可怕的字母代码从 90 秒优化到了 9 秒。有人曾经告诉我“永远不要在循环中执行 SQL 语句”,这让我很受不了。我知道我必须找到更好的方法,所以 - 干杯!
    • 当我尝试编写 update_columns 时,我不断收到AttributeError: 'Insert' object has no attribute 'excluded'。我的插入语句如下所示:insert_statement = insert(table).values(items) 其中 table 是 DeclarativeMeta 类型,items 是字典列表。任何建议将不胜感激。
    • 我想通了...对于遇到此问题的任何人,请特别注意从哪里导入插入。它应该是 from sqlalchemy.dialects.postgresql import insert 而不是 from sqlalchemy import insert。上帝。
    • 是的,对于这样的数据库特定结构,您必须直接从方言导入。
    猜你喜欢
    • 2020-06-03
    • 2017-06-11
    • 2016-11-29
    • 2013-12-07
    • 2023-04-01
    • 1970-01-01
    • 2013-05-19
    • 2014-10-30
    • 1970-01-01
    相关资源
    最近更新 更多