PostgreSQL含UNION的UPSERT为何偶尔返回空结果?
问题原因分析
出现空结果的核心原因是并发事务竞态条件结合PostgreSQL默认的READ COMMITTED事务隔离级别,具体触发场景如下:
当两个事务(事务A、事务B)同时针对同一个
col1值执行该UPSERT语句时:- 事务A执行
INSERT ... ON CONFLICT DO NOTHING,此时无已提交的冲突行,插入成功但暂未提交。 - 事务B执行
INSERT操作时,因事务A的插入未提交,在READ COMMITTED隔离级别下无法看到该行,因此尝试插入,但PostgreSQL会因唯一约束限制,等待事务A提交后再判定冲突。 - 事务A提交后,事务B的
INSERT触发唯一约束冲突,执行DO NOTHING,此时CTE部分upsert_result为空。 - 事务B执行后续的
SELECT * FROM table1 WHERE col1=$1时,READ COMMITTED隔离级别下该查询仅能看到事务B启动前已提交的数据,无法读取事务A刚提交的新行,因此该SELECT也返回空。 - 最终整个语句的
UNION结果为空。
- 事务A执行
极端场景:若在当前语句执行期间,有其他事务删除了
col1对应的行并提交,也可能导致INSERT因暂未感知到删除而触发冲突执行DO NOTHING,后续SELECT时该行已被删除,从而返回空结果。
内容的提问来源于stack exchange,提问作者Marumba
相关产品推荐
相关产品推荐

