sqlalchemy 如何写“插入或更新”并返回 upsert 结果
技术百科
舞夢輝影
发布时间:2026-01-27
浏览: 次 SQLAlchemy 2.0+ 中 PostgreSQL 用 on_conflict_do_update(index_elements=[...]) 实现 upsert,需严格匹配唯一约束字段;SQLite 用 on_conflict_do_replace();MySQL 需手动 prefix_with("ON DUPLICATE KEY UPDATE") 并查 ROW_COUNT();返回完整记录须配合 returning() 且注意数据库版本与 ORM 刷新问题。
SQLAlchemy 2.0+ 的 insert().on_conflict_do_update() 怎么用
PostgreSQL 和 SQLite(3.24+)原生支持 upsert,SQLAlchemy 2.0+ 将其封装为 on_conflict_do_update()(PostgreSQL)和 on_conflict_do_replace()(SQLite)。关键不是“有没有”,而是“冲突键怎么写对”——漏掉 index_elements 或写错字段名,会直接报 IntegrityError 而非执行更新。
示例:用户表按 email 唯一约束做 upsert:
from sqlalchemy.dialects.postgresql import insertstmt = insert(User).values( email="alice@example.com", name="Alice", updated_at=func.now() ) stmt = stmt.on_conflict_do_update( indexelements=["email"], # 必须匹配唯一索引/主键字段,不能写成 ["id"] 除非是主键冲突 set=dict( name=stmt.excluded.name, updated_at=func.now() ) ) session.execute(stmt) session.commit()
-
index_elements不是表字段任意组合,必须严格对应数据库中已存在的唯一约束或主键定义 -
stmt.excluded是 PostgreSQL 特有命名,指代本次 INSERT 中被拒绝的那行数据;SQLite 用excluded表达相同语义,但需确认驱动版本 - MySQL 没有原生
ON CONFLICT,得退回到INSERT ... ON DUPLICATE KEY UPDATE方式(见下节)
MySQL 怎么写等效的 upsert 并拿到影响行数
MySQL 不支持 on_conflict_do_update(),必须用 prefix_with("ON DUPLICATE KEY UPDATE") 手动拼接。更麻烦的是:SQLAlchemy 默认不暴露 MySQL 的 ROW_COUNT(),session.execute() 返回的 Result 对象里没有“到底插入了还是更新了”的明确标识。
实操建议:
- 在
INSERT语句末尾加prefix_with("ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id)"),再调用connection.exec_driver_sql("SELECT ROW_COUNT()").scalar()获取实际影响行数(1=插入,2=更新) - 更稳的方式是先
SELECT查是否存在,再分两路处理——虽然多一次查询,但逻辑清晰、可测、不依赖数据库返回值解释 - 别信
session.merge():它内部是先查后 insert/update,且无法区分操作类型,也不保证原子性
如何让 upsert 返回完整记录(比如新生成的 id 或更新后的值)
PostgreSQL 支持 RETURNING,但必须和 on_conflict_do_update() 配合使用,且只对“最终生效的行”返回——即:插入成功时返回新行,更新成功时返回更新后的行。
写法要点:
-
returning()必须链在on_conflict_do_update()之后,不能放在insert()后直接调 - 返回字段要明确写,比如
returning(User.id, User.name, User.updated_at);用User类本身会返回整行,但 ORM 映射可能不自动刷新 - 执行后用
result.fetchone()拿结果,不是scalar()——后者只适合单列
常见坑:returning() 在 SQLite 中仅限 3.35+ 且需启用 sqlite:///...?enable_returning=true 参数,否则静默失败。
ORM 层做 upsert 容易忽略的事务与刷新问题
直接用 session.execute(insert_stmt) 是最可控的,但如果你坚持用 ORM 实例(如 session.merge(user) 或 session.add(user)),要注意:
-
merge()不触发数据库级唯一冲突检测,它只基于 Python 对象 identity map 做判断,一旦并发写入就可能丢更新 -
add()+flush()后如果遇到唯一冲突,会抛IntegrityError,但此时 session 状态已脏,必须session.rollback()后重试,不能简单捕获后继
续
- 即使 upsert 成功,ORM 实例的属性也不会自动同步数据库返回值(比如自增
id或服务端默认值),得手动session.refresh(instance)
真正需要 upsert 语义时,绕过 ORM 直接写 Core 语句反而更轻、更准、更容易调试。
# ai
# python
# 封装
# session
# select
# mysql
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Win11怎么设置虚拟桌面 Win11新建多桌面切
- 如何减少Golang内存碎片化_Golang内存分
- C++如何将C风格字符串(char*)转换为std
- LINUX的SELinux是什么_详解LINUX强
- 如何使用Golang benchmark测量函数延
- Mac如何整理桌面文件_Mac使用堆栈功能一键整理
- Windows 11怎么更改锁屏超时时间_Wind
- 如何在Golang中处理JSON字段缺失_Gola
- 如何在Golang中理解指针比较_Golang地址
- Win11怎么设置多显示器任务栏 Win11扩展任
- Windows10如何更改日期格式_Win10区域
- Python对象比较排序规则_集合使用说明【指导】
- Windows10系统怎么查看硬盘健康_Win10
- Windows10怎么用“讲述人”读屏辅助 Win
- c# 在高并发下使用反射发射(Reflection
- c++的static关键字有什么用 静态变量和静态
- Win11怎么更改电脑密码_Windows 11修
- Python装饰器设计思路_功能增强机制说明【指导
- c++ reinterpret_cast怎么用 c
- 短链接怎么用php递归还原_多层加密链接的处理法【
- Win11怎么清理C盘系统日志_Win11清理系统
- Win11怎么设置应用分屏_Windows11贴靠
- php本地部署后数据库连接报错_1045acces
- Win11怎么设置ip地址_Windows 11手
- 如何在Golang中处理URL参数_Golang
- c++如何用AFL++进行模糊测试 c++ Fuz
- c++如何使用std::bind绑定函数参数_c+
- LINUX下如何配置VLAN虚拟局域网_在LINU
- Win11怎么关闭防火墙通知_屏蔽Win11安全中
- c++中如何使用虚函数实现多态_c++多态性实现原
- php485能和物联网模块通信吗_php485对接
- C++如何解析JSON数据?(nlohmann/j
- Win11怎么退出高对比度模式_Win11取消反色
- Windows10如何更改鼠标图标_Win10鼠标
- Win11无法安装软件怎么办_Win11解除应用安
- Win11怎么设置默认邮件客户端 Win11修改M
- php与c语言在嵌入式中有何区别_对比两者在硬件控
- 如何使用Golang构建简易投票统计功能_Gola
- Win11任务栏怎么调到左边_Win11开始菜单居
- 新手学PHP架构总混淆概念咋办_重点梳理【教程】
- c++的位运算怎么用 与、或、异或、移位操作详解【
- Windows 11如何查看系统激活密钥_Wind
- 如何用::实现单例模式_php静态方法与作用域操作
- Python生成器表达式内存优化_惰性计算说明【指
- PHP主流架构如何处理会话管理_Session与C
- php485支持哪些操作系统_php485跨系统支
- 短链接还原php提示内存不足_调整PHP内存限制设
- Windows10如何更改开机密码_Win10登录
- Win11怎么用设置清理回收站_Win11设置清理
- Win11 C盘满了怎么清理 Win11磁盘清理和


QQ客服