当前位置: 首页 > news >正文

【Python】SQLAlchemy实现upsert

文章目录

    • ✅ 通用思路
      • 1. 使用 `merge()` 方法(适用于简单场景)
      • 2. 使用数据库特定的 UPSERT 功能(推荐用于性能和并发安全)
        • 🟢 PostgreSQL: 使用 `on_conflict_do_update`
        • 🟡 MySQL: 使用 `ON DUPLICATE KEY UPDATE`
        • 🔵 SQLite: 使用 `ON CONFLICT`
    • 💡 小技巧:使用 `add()` + `flush()` + 捕获异常(不推荐用于高并发)
    • 📝 总结

SQLAlchemy 中,实现 UPSERT(Update or Insert) 的方式取决于你使用的数据库类型(如 PostgreSQLMySQLSQLite 等),因为不同数据库对 UPSERT 的支持语法不同。下面介绍几种常见的方式:


✅ 通用思路

SQLAlchemy 没有直接提供 upsert 方法,但可以通过以下方式实现:

1. 使用 merge() 方法(适用于简单场景)

merge() 方法用于将给定实例的状态复制到当前Session中的相应实例中。如果在当前Session本地找不到目标实例,它会尝试根据主键从数据库加载实例,如果还找不到对象,则创建一个新实例。然后,源实例上每个属性的状态将复制到目标实例。然后,该方法返回生成的目标实例

from sqlalchemy.orm import Session
from your_model_file import YourModelobj = YourModel(id=1, name="John Doe")  # id 是主键
with Session() as session: # 使用上下文管理器自动处理 Session 的创建和关闭,如果有异常会自动回滚session.merge(obj) # 插入或更新数据库session.commit() # 刷新挂起的更改并提交当前事务

🔍 merge() 会根据主键或唯一约束判断是否已存在记录:

  • 如果存在,则更新;
  • 如果不存在,则插入。

⚠️ 注意:merge() 会创建一个新的实例并返回,并不会修改原对象。


2. 使用数据库特定的 UPSERT 功能(推荐用于性能和并发安全)

🟢 PostgreSQL: 使用 on_conflict_do_update
from sqlalchemy.dialects.postgresql import insertstmt = insert(YourModel).values(id=1, name='John Doe')
stmt = stmt.on_conflict_do_update(index_elements=[YourModel.id],  # 主键或唯一索引字段set_=dict(name='John Doe')      # 要更新的字段
)session.execute(stmt)
session.commit()

🟡 MySQL: 使用 ON DUPLICATE KEY UPDATE
from sqlalchemy.dialects.mysql import insertstmt = insert(YourModel).values(id=1, name='John Doe')
stmt = stmt.on_duplicate_key_update(name='John Doe')session.execute(stmt)
session.commit()

🔵 SQLite: 使用 ON CONFLICT

SQLite 支持 ON CONFLICT DO UPDATE(从 3.24 开始):

from sqlalchemy.dialects.sqlite import insertstmt = insert(YourModel).values(id=1, name='John Doe')
stmt = stmt.on_conflict_do_update(index_elements=[YourModel.id],set_=dict(name='John Doe')
)session.execute(stmt)
session.commit()

💡 小技巧:使用 add() + flush() + 捕获异常(不推荐用于高并发)

如果你不想用数据库特定语法,也可以尝试先 add,捕获唯一冲突异常后再 update:

try:session.add(obj)session.flush()
except IntegrityError:session.rollback()existing = session.query(YourModel).get(obj.id)for key, value in obj.__dict__.items():if not key.startswith('_'):setattr(existing, key, value)session.commit()

⚠️ 不推荐这种方式,因为并发时容易出错,且效率不高。


📝 总结

方法数据库兼容性推荐程度
merge()兼容所有数据库⭐⭐
on_conflict_do_update (PostgreSQL/SQLite)PostgreSQL >= 9.5 / SQLite >= 3.24⭐⭐⭐⭐
on_duplicate_key_update (MySQL)MySQL⭐⭐⭐⭐
手动捕获异常兼容性强

http://www.dtcms.com/a/285692.html

相关文章:

  • 【Linux网络编程】应用层协议 - HTTP
  • uniapp 动态控制横屏(APP 端)
  • Bitbucket平台的HTTP Access Tokens操作手册
  • 开发避坑短篇(2):uni-app微信小程序开发‘createIndependentPlugin‘模块缺失问题分析与解决方案
  • 创蓝闪验SDK适配uniappx版本UTS插件集成文档
  • Redis缓存双写的学习(五)
  • Python暑期学习笔记5
  • 平板可以用来办公吗?从文档处理到创意创作的全面测评
  • JavaScript 的垃圾回收机制
  • 第 14 章 线性回归预测策略----SPL量化编程课
  • CUPED (Controlled-experiment using Pre-Experiment Data) 论文学习笔记
  • 软删除设计:为什么使用 deleted_at = ‘1970-01-01 00:00:00‘ 表示未删除?
  • 1-大语言模型—理论基础:详解Transformer架构的实现(1)
  • 零信任产品联合宁盾泛终端网络准入,打造随需而变、精准贴合业务的网络安全访问体系
  • python爬虫获取PDF
  • pdf格式怎么提取其中一部分张页?
  • PDF 拆分合并PDFSam:开源免费 多文件合并 + 按页码拆分 本地处理
  • Elasticsearch 和 solr 的区别
  • 【Docker#2】容器历史发展 | 虚拟化实现方式
  • C# 转换(引用转换)
  • uni-app 跳转页面传参
  • Docker --privileged 命令详解
  • Diffusion-VLA 中的 Reasoning Token 注入机制解析:语言推理如何控制扩散模型?
  • 2.3 前端-ts的接口以及自定义类型
  • k8s:手动创建PV,解决postgis数据库本地永久存储
  • 【unity实战】使用unity的Navigation+LineRenderer实现一个3D人物寻路提前指示预测移动轨迹的效果,并可以适配不同的地形
  • 搜索引擎优化全攻略:提升百度排名优化
  • 解决Flutter运行android提示Deprecated imperative apply of Flutter‘s Gradle plugins
  • 武汉江滩某码头变形及应力自动化监测
  • 麒麟操作系统unity适配