简体   繁体   English

使用SQLAlchemy Postgres进行批量Upsert

[英]Bulk Upsert with SQLAlchemy Postgres

I'm following the SQLAlchemy documentation here to write a bulk upsert statement with Postgres. 我在这里遵循SQLAlchemy文档,用Postgres编写批量upsert语句。 For demonstration purposes, I have a simple table MyTable : 出于演示目的,我有一个简单的表MyTable

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

Creating a generic insert statement is simple enough: 创建通用插入语句很简单:

from sqlalchemy.dialects import postgresql

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

The problem I run into is when I try to add the "on conflict" part of the upsert. 我遇到的问题是当我尝试添加upsert的“on conflict”部分时。

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

Trying to execute this statement yields a ProgrammingError : 尝试执行此语句会产生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'

I think my misunderstanding is in constructing the statement with the on_conflict_do_update method. 我认为我的误解是使用on_conflict_do_update方法构造语句。 Does anyone know how to construct this statement? 有谁知道如何构建这个陈述? I have looked at other questions on StackOverflow (eg. here ) but I can't seem to a way to address the above error. 我已经查看了StackOverflow上的其他问题(例如这里 ),但我似乎无法解决上述错误。

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

index_elements should either be a list of strings or a list of column objects. index_elements应该是字符串列表或列对象列表。 So either [MyTable.id] or ['id'] (This is correct) 那么[MyTable.id]['id'] (这是正确的)

set_ should be a dictionary with column names as keys and valid sql update objects as values. set_应该是一个字典,列名作为键,有效的sql更新对象作为值。 You can reference values from the insert block using the excluded attribute. 您可以使用excluded属性引用插入块中的值。 So to get the result you are hoping for here you would want set_={'test_value': insert_stmt.excluded.test_value} (The error you made is that data= in the example isn't a magic argument... it was the name of the column on their example table) 所以为了获得你希望在这里得到的结果,你需要set_={'test_value': insert_stmt.excluded.test_value} (你set_={'test_value': insert_stmt.excluded.test_value}的错误是data=在示例中不是一个神奇的参数......它是示例表上列的名称)

So, the whole thing would be 所以,整件事情都会如此

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

Of course, in a real world example I usually want to change more then one column. 当然,在现实世界的例子中,我通常想要更改一列。 In that case I would do something like... 在那种情况下,我会做...

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)

(This example would overwrite every column except for the id and datetime_created columns) (此示例将覆盖除id和datetime_created列之外的每个列)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM