PostgreSQL存储过程/服务函数逻辑块是顺序执行还是类似CTE并行?
PostgreSQL PL/pgSQL 语句块执行规则说明
可写CTE异常的本质
CTE场景下出现的跨CTE无法调用、更新结果不符合预期,是PostgreSQL的固有机制决定,并非执行顺序随机错乱:
- 单条SQL语句内的所有CTE、主语句共享同一个语句级快照,所有子句仅能读取该SQL启动前已提交的持久化数据,无法读取同一条SQL内其他CTE产生的写入修改。
- 包含INSERT/UPDATE/DELETE的可写CTE,执行逻辑受MVCC规则约束,天然不支持同语句内跨CTE读取写入结果,和书写顺序无直接关联。
对应问题示例代码:
WITH insert_cte AS ( INSERT INTO some_table SELECT * FROM (...) AS some_values_from_first_relationship ) UPDATE some_table -- 此处无法读取insert_cte写入的数据,二者属于同一条SQL,共享语句启动时的初始快照 SET <some_values_from_first_and_second_relation_ship>
PL/pgSQL嵌套块的执行特性
通过自定义函数/存储过程实现的多BEGIN - END块拆分写法,会严格按书写顺序从上到下执行,不会出现CTE场景下的执行结果不符合预期的问题:
<<main_label>> BEGIN <<insert_cte_analogue>> BEGIN [insert_cte_logic] END; <<update_cte_analogue>> BEGIN [update_cte_logic] END; END;
核心执行规则:
- 同一事务上下文内,前一个语句块内的所有逻辑执行完毕后,才会启动下一个语句块的执行,不存在执行顺序被优化器重排的情况。
- 前一个块执行完成后,块内产生的所有数据增删改结果,对后续块内的SQL语句完全可见,可以直接读取使用,满足先插入、后基于插入结果更新的业务需求。
- 语句块标签仅用于标记变量作用域、支持跳转控制,不会对执行顺序产生任何影响。
边界注意事项
两个容易引发逻辑异常的场景需要提前规避:
- 若某个
BEGIN...END块配置了EXCEPTION异常捕获子句,该块会作为独立子事务运行:块内抛出的异常被捕获时,块内所有已执行的数据修改都会被回滚,不会传递到后续块。- 不要将插入、更新逻辑合并为单条带CTE的SQL放在同一个块内,只要将两类操作拆为独立SQL分属前后块,执行顺序就完全可控。
内容的提问来源于stack exchange,提问作者Prosto_Oleg
相关产品推荐
相关产品推荐

