使用SQLAlchemy Core中的WHERE进行批量更新

51

我已经成功地在SQLAlchemy中使用了批量插入,如下所示:

conn.execute(addresses.insert(), [ 
   {'user_id': 1, 'email_address' : 'jack@yahoo.com'},
   {'user_id': 1, 'email_address' : 'jack@msn.com'},
   {'user_id': 2, 'email_address' : 'www@www.org'},
   {'user_id': 2, 'email_address' : 'wendy@aol.com'},
])

现在我需要的是与“更新”等效的东西。 我试过这个:

conn.execute(addresses.insert(), [ 
   {'user_id': 1, 'email_address' : 'jack@yahoo.com', 'id':12},
   {'user_id': 1, 'email_address' : 'jack@msn.com', 'id':13},
   {'user_id': 2, 'email_address' : 'www@www.org', 'id':14},
   {'user_id': 2, 'email_address' : 'wendy@aol.com', 'id':15},
])

期望根据“id”字段更新每一行,但失败了。我猜原因是没有指定WHERE子句,但不知道如何使用包含在字典中的数据来指定WHERE子句。

有人能帮我吗?

5个回答

89

阅读教程中的使用Core更新和删除行部分。以下代码可帮助您入门:

from sqlalchemy import bindparam
stmt = addresses.update().\
    where(addresses.c.id == bindparam('_id')).\
    values({
        'user_id': bindparam('user_id'),
        'email_address': bindparam('email_address'),
    })

conn.execute(stmt, [
    {'user_id': 1, 'email_address' : 'jack@yahoo.com', '_id':1},
    {'user_id': 1, 'email_address' : 'jack@msn.com', '_id':2},
    {'user_id': 2, 'email_address' : 'www@www.org', '_id':3},
    {'user_id': 2, 'email_address' : 'wendy@aol.com', '_id':4},
])

1
插入操作也依赖于多个查询,怎么办? - Aidis
“update() missing 1 required positional argument: 'values'” 这个错误信息是什么意思?在这个例子中,“addresses”指的是什么? - Hedde van der Heide
3
@HeddevanderHeide在Table实例中的addresses指的是什么。 - van
1
这很慢。 同样地,对于插入操作,我注意到 conn.execute(table.insert().values([...]))conn.execute(table.insert(), [...]) 快得多。 是否有类似的方法可以用于更新以提高速度? - nbarraille
2
我认为更新的链接现在是Insert, Updates, Deletes - Jens
显示剩余5条评论

32

会话(session)有一个名为bulk_insert_mappingsbulk_update_mappings的函数: 文档.

请注意,您必须在映射中提供主键(primary key)

# List of dictionary including primary key
user_mappings = [{
    'user_id': 1, # This is pk?
    'email_address': 'jack@yahoo.com',
    '_id': 1
}, ...]

session.bulk_update_mappings(User, user_mappings)
session.commit()

2
对于具有复合主键(v_id,order)的情况,此方法对我无效。 - Nick Woodhams
1
@NickWoodhams 在我的情况下完美地工作,使用复合主键(Col1,col2)。你遇到了什么错误? - Rohit Lal
1
请注意,问题要求使用SQLAlchemy核心来解决。而这个解决方案使用的是SQLAlchemy ORM,而不是核心。 - Guybrush

4

在2023年,SQLAlchemy 2.0 更改了API

from sqlalchemy import update
session.execute(
    update(User),
    [
        {"id": 1, "fullname": "Spongebob Squarepants"},
        {"id": 3, "fullname": "Patrick Star"},
        {"id": 5, "fullname": "Eugene H. Krabs"},
    ],
)

3

@Jongbin Park的解决方案对我来说非常有效,尤其是在具有复合主键的情况下。(Azure SQL Server)。

update_vals = []
update_vals.append(dict(Name='name_a', StartDate='2020-05-26 20:17:32', EndDate='2020-05-26 20:46:03', Comment='TEST COMMENT 1'))
update_vals.append(dict(Name='name_b', StartDate='2020-05-26 21:31:16', EndDate='2020-05-26 21:38:37', Comment='TEST COMMENT 2'))
s.bulk_update_mappings(MyTable, update_vals)
s.commit()

其中,Name、StartDate 和 EndDate 都是复合主键的一部分。'Comment' 是要在数据库中更新的值。


0
使用上述答案确实起作用,因为SQLAlchemy声称您需要添加synchronize_session=None。 我使用了以下代码来烹饪正确的菜谱。
values= {"id" : results.id, "user_id": results.id}
        if user.email is not None:
            values['email'] = user.email
        if user.first_name is not None:
            values['first_name'] = user.first_name

        if user.last_name is not None:
            values['last_name'] = user.last_name
        else:
            values['last_name'] = results.last_name
update_user_q = update(u).where(u.id == bindparam("user_id")).values(
                            email=bindparam("email"),
                            first_name=bindparam("first_name"), last_name=bindparam("last_name")).execution_options(
                            synchronize_session=None)
            db.execute(update_user_q, [
                values]
                )
            db.commit()

别忘了在你的数据中传递主要的内容

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接