使用SQLAlchemy Postgres批量上传

use*_*153 4 python postgresql sqlalchemy upsert

我在这里遵循SQLAlchemy文档以使用Postgres编写批量upsert语句。为了演示,我有一个简单的表MyTable

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

创建通用插入语句非常简单:

from sqlalchemy.dialects import postgresql

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

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

update_stmt = insert_stmt.on_conflict_do_update(
    index_elements=[MyTable.id],
    set_=dict(data=values)
)
Run Code Online (Sandbox Code Playgroud)

尝试执行此语句会产生一个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'
Run Code Online (Sandbox Code Playgroud)

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

Pau*_*tte 8

update_stmt = insert_stmt.on_conflict_do_update(
    index_elements=[MyTable.id],
    set_=dict(data=values)
)
Run Code Online (Sandbox Code Playgroud)

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}
)
Run Code Online (Sandbox Code Playgroud)

当然,在一个现实世界的示例中,我通常要更改多于一列的内容。在这种情况下,我会做类似...

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)
Run Code Online (Sandbox Code Playgroud)

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

  • 我想通了...对于遇到此问题的任何人,请特别注意导入插入的位置。它应该是“from sqlalchemy.dialects.postgresql import insert”而不是“from sqlalchemy import insert”。上帝。 (5认同)
  • 这将我可怕的 alpha 代码从 90 秒优化到了 9 秒。有人曾经告诉我“永远不要在循环中执行 SQL 语句”,这句话一直困扰着我。我知道我必须找到更好的方法,所以——干杯! (4认同)
  • 很好的答案 - 超级清晰且易于理解。谢谢! (2认同)