PostgreSQL如何高效执行UPSERT操作_利用ON CONFLICT指令
PostgreSQL ON CONFLICT:唯一可靠的原子UPSERT操作指南

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
在PostgreSQL的世界里,如果你想实现“有则更新,无则插入”的UPSERT操作,ON CONFLICT是那条唯一可靠、原子且可预测的路径。至于那些在业务层自己写的“先查询,再决定插入或更新”的逻辑,在并发场景下几乎注定会出问题,建议直接放弃尝试。
ON CONFLICT 必须基于唯一约束,否则直接报错
这里有个关键点需要明确:当你写下ON CONFLICT (email)时,PostgreSQL并不会自动为你创建索引。它只会检查email这一列是否已经定义了UNIQUE约束或是PRIMARY KEY。如果没有,那么等待你的将是明确的错误提示:
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
哪些是常见的误解和误操作呢?
- 对着一张表的普通
INDEX(非唯一索引)使用ON CONFLICT——行不通。 - 对着一个仅有
NOT NULL属性的列使用——同样行不通。 - 表刚创建完,忘了给目标列加上
UNIQUE(email)约束就直接执行SQL——结果就是报错。
验证方法其实很简单:在psql命令行中执行\d 表名,确认目标列旁边清晰地标记着UNIQUE或PK。
多唯一约束时,必须显式指定冲突目标
想象一张表同时拥有email UNIQUE、phone UNIQUE和id PRIMARY KEY三个唯一约束。如果你只写了ON CONFLICT (id),那么当email发生冲突时,语句依然会报错,而不会自动回退到其他约束进行处理。
正确的做法是二选一:
- 使用列名:如果冲突只可能来自主键,那就明确指定
ON CONFLICT (id)。 - 使用约束名:这种方式通常更安全,尤其当你需要响应特定列(如
email)的冲突时。首先查询约束名:SELECT conname FROM pg_constraint WHERE conrelid = 'users'::regclass AND contype = 'u';,然后在语句中引用:ON CONFLICT ON CONSTRAINT users_email_key。 - 对于联合唯一约束,逻辑相同:例如定义了
UNIQUE(user_id, product_id),就必须写ON CONFLICT (user_id, product_id)。
DO UPDATE 里用 WHERE 条件要格外小心
很多人习惯在DO UPDATE后面加上WHERE products.status = 'active'这样的条件,意图是“只更新状态为有效的商品”。但这里有个至关重要的细节:这个WHERE子句是作用于原表行(即发生冲突的那一行)的,而不是作用于准备插入的新数据。一旦条件不成立,整个DO UPDATE操作就会被跳过——结果就是既没有插入新行,也没有更新旧行。更棘手的是,语句还会返回成功(例如INSERT 0 1),这种“静默失效”非常隐蔽,难以排查。
容易踩到的坑包括:
- 忘记加
WHERE条件,导致意外覆盖了历史状态。 - 加了
WHERE条件,却没意识到它可能导致整个UPSERT操作静默失败。 - 在
DO UPDATE SET中引用EXCLUDED虚拟表来获取新值时,误写成MySQL风格的VALUES()语法(PostgreSQL不支持这种写法)。
来看一个正确的写法示例:
INSERT INTO products (sku, price, status) VALUES ('IPHONE_15', 7999, 'active') ON CONFLICT (sku) DO UPDATE SET price = EXCLUDED.price, updated_at = NOW() WHERE products.status = 'active';
想拿到插入或更新后的 ID?RETURNING 必须配 QueryRow
这个场景很常见:在Go或Python程序中,开发者使用db.Query(... RETURNING id),然后直接调用rows.Scan(&id),却发现返回的id总是0。问题出在哪里?因为RETURNING子句最多只返回一行,而像Go的db.Query()方法要求必须先调用rows.Next()才能读取数据。
真正安全的做法只有这一种:
- Go语言:必须使用
db.QueryRow()。这个方法内部自动处理了单行结果集的逻辑,如果查询无结果,会返回sql.ErrNoRows错误。 - Python (psycopg2):使用
cursor.fetchone(),不要使用cursor.fetchall()。 - psql命令行:直接使用
RETURNING *没问题,但在应用程序中不能假设总是返回多行。
还有一个最容易被忽略的关键点:即使语句触发了DO UPDATE(即执行了更新),RETURNING子句返回的也始终是当前行(即更新后的行)的值,而不是最初尝试插入的那些值。这一点在设计幂等性写入逻辑时,对于调试和理解行为至关重要。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
Oracle分区表物化视图如何支持高并发_优化锁资源竞争
Oracle物化视图FAST REFRESH默认锁整分区表,因物化视图日志缺失分区键信息,无法定位变更分区;需同时满足日志含分区键列且MV定义显式引用该列,才能实现分区粒度加锁。 物化视图刷新时为什么会锁定整个分区表? 许多Oracle DBA都曾面临一个典型问题:在执行分区表的物化视图FAST R
如何处理SQL语句中的HEX编码注入绕过_对输入流进行16进制检测
HEX编码绕过:当十六进制字面量成为SQL注入的“隐身衣” 在安全对抗的战场上,攻击者的手法总是层出不穷。其中,利用十六进制(HEX)编码绕过传统的关键字和符号过滤,已经成为一种相当经典且有效的SQL注入手段。这背后的原理并不复杂,但防御起来却需要格外细致的考量。 HEX编码在SQL注入中怎么被用来
Oracle RMAN备份加密如何配置_通过配置备份加密增强安全性
RMAN备份加密:那些容易被忽略的配置陷阱与性能真相 说到RMAN备份加密,一个常见的误解是“配置了就能自动生效”。事实并非如此,关键在于必须清晰区分configure encryption for database on(全局策略)和set encryption on identified by(
SQL怎样实现类似Excel透视表的功能_利用CASE WHEN行转列
SQL怎样实现类似Excel透视表的功能_利用CASE WHEN行转列 SQL里用CASE WHEN做行转列,本质是聚合+条件判断 开门见山,先说核心:CASE WHEN这个语句本身并不产生“转列”的魔法。它必须和GROUP BY以及聚合函数(比如SUM、COUNT)联手,才能模拟出Excel透视表
如何解决ORA-12541无监听程序_lsnrctl status排查流程
ORA-12541 连接失败深度解析:监听器未启动是主因,系统化排查从状态检查到网络验证 ORA-12541 报错时,先确认监听器进程是否真的在运行 当数据库连接出现 ORA-12541 错误时,许多用户会首先怀疑 tnsnames ora 配置或服务名设置。实际上,该错误的根本原因在于客户端无法与
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

