给公共表表达式 CTE 加上 NOT MATERIALIZED,并不是给查询打开一个通用的加速选项。如果这份结果被使用多次,而且里面含有耗时计算,减少一层独立结果的同时,也可能失去计算结果的复用。

PostgreSQL 17 官方文档为讨论划出了前提:CTE 必须是非递归、无副作用的 SELECT;这里的无副作用指不包含 volatile 函数。在此前提下,CTE 可以合并到外层查询,让两层一起优化。默认情况下,外层只引用一次时会这样处理;引用多次时,默认保留独立计算。MATERIALIZED 和 NOT MATERIALIZED 可分别要求独立计算或合并,不能把这一规则直接套到所有 WITH 语句。

为什么多次引用值得单独考虑?官方示例先在 CTE 中为数据行调用一个昂贵函数,再用两个别名引用该 CTE,并按函数结果连接。保留独立计算时,每行的函数结果可以供这两次引用使用,避免对同一行算两遍。若改为合并到外层,便可能出现重复求值。这里描述的是文档示例的计算关系,本文没有执行 SQL,也没有测得任何耗时差异。

但复用也有代价。文档说明,面对多次引用的 CTE,优化器不能随意把外层某一次使用的筛选条件推到共同结果里,因为其他引用未必应受同一条件限制。这样一来,外层随后不要的行,也可能已进入 CTE 的计算范围。独立计算并不意味着所有场景都必须读完全部行;文档同时保留了上层只索取有限行时可能提前停止的情况。

这就形成了两个相反的方向:若各次引用只需要完整结果中的一小部分,合并后允许各自筛选,可能省下更多工作;若各处都需要同一批昂贵计算的结果,保留复用可能更有价值。实际判断应关注“少处理多少行”和“多算多少次”,不能只数 SQL 中有几层括号。

阅读执行计划时也要区分证据。普通 EXPLAIN 展示计划器选择的计划;带 ANALYZE 选项则会真正执行所给语句,并加入运行统计,而且测量本身有开销。因此,一份计划说明与一份实测耗时并非同一种结论,本篇没有提供后者。

若要整理方案比较,可记录 CTE 被引用的位置、各处需要的行以及哪些表达式成本较高,再解释改变合并方式可能影响哪一项。以上依据2026年10月12日读取的 PostgreSQL 17 文档,限于上述 SELECT 情境,不构成真实数据库的改写建议或性能保证。

信息来源

本文基于上述公开资料整理,未使用来源页面的图片、视频或嵌入媒体。