0

0

sqlalchemy 如何写“批量更新”且只更新变化的字段

冷漠man

冷漠man

发布时间:2026-01-23 20:37:26

|

483人浏览过

|

来源于php中文网

原创

SQLAlchemy批量更新无法自动只改变化字段,必须手动比对新旧值并构造差异字典传给bulk_update_mappings();若需ORM事件或默认值计算,则应使用merge()或逐个setattr后flush()。

sqlalchemy 如何写“批量更新”且只更新变化的字段

SQLAlchemy 批量更新只改变化字段,用 update() + values() 不行

直接用 session.execute(update(...).values(...)) 是全量覆盖——哪怕你只传了 1 个字段,它也会把其他字段设为 NULL 或默认值(取决于数据库行为),根本不是“只更新变化字段”。这不是 bug,是 SQL 标准语义:UPDATE 语句本身不感知“原始值”,它只按你写的 SET 子句执行。

真正可行的方案:用 session.bulk_update_mappings() + 差异预计算

SQLAlchemy 原生不提供“自动 diff 字段后生成条件化 UPDATE”的功能。必须自己比对新旧值,构造仅含变化字段的字典。核心是两步:先查出原数据(或缓存旧值),再构造只含差异的 dict 列表传给 bulk_update_mappings()

  • 必须带主键(如 id)在 mapping 字典里,否则无法定位行
  • 只写你想改的字段,没出现的字段完全不会出现在 SET 子句中
  • 所有 mapping 字典的键名必须一致(不能有的写 user_name,有的写 username
  • 不触发 ORM 事件(如 @validatesbefore_update),也不走属性 setter
# 示例:只更新 name 和 email 有变化的用户
old_users = session.query(User).filter(User.id.in_([1, 2, 3])).all()
updates = []
for u in old_users:
    new_data = get_new_user_data(u.id)  # 你的业务逻辑,返回 dict
    changed = {k: v for k, v in new_data.items() if getattr(u, k) != v}
    if changed:
        changed["id"] = u.id  # 主键必须存在
        updates.append(changed)

if updates: session.bulk_update_mappings(User, updates) session.commit()

想保留 ORM 行为?只能单条处理 + session.merge() 或手动 set

如果依赖 @validates、关系级联、default/server_default 计算,或者需要触发 before_update,就别强求批量。老实用循环 + merge() 或显式赋值:

Kive
Kive

一站式AI图像生成和管理平台

下载
  • session.merge() 会查库比对,只发变更字段的 UPDATE(但本质是 1 次 SELECT + 1 次 UPDATE)
  • 更轻量的是直接查出对象、逐个 setattr()、然后 session.flush() —— 只要没调 commit(),仍是单次事务内的多条 UPDATE
  • 注意:merge() 对无主键或复合主键场景行为复杂,慎用
# 安全可控的做法(推荐用于中小批量)
users = session.query(User).filter(User.id.in_([1, 2, 3])).all()
for u in users:
    new_vals = get_new_user_data(u.id)
    for field, val in new_vals.items():
        if getattr(u, field) != val:
            setattr(u, field, val)
session.flush()  # 不 commit,留到外层统一提交

绕不开的坑:数据库层面的并发更新风险

无论用哪种方式,“先查再比再更”都不是原子操作。两个并发请求同时读到同一旧值,各自算出不同变更集并写入,可能丢失某一方的修改。真要强一致性,得加 WHERE 条件校验原始值(即“乐观锁”):

  • 在 UPDATE 语句里显式加上 WHERE name = 'old' AND email = 'old@x.com'
  • SQLAlchemy 中需手写 update(...).where(...),无法用 bulk_update_mappings 实现
  • 或者加版本列(version_id_col),用 update(...).where(Model.version == old_ver)

差分更新本身不解决并发问题,这点常被忽略——业务上是否允许“最后写入获胜”,得提前想清楚。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

685

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

323

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

348

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1117

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

359

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

717

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

577

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

419

2024.04.29

c++空格相关教程合集
c++空格相关教程合集

本专题整合了c++空格相关教程,阅读专题下面的文章了解更多详细内容。

0

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 49.9万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号