SQL CTE 一定会物化吗?MATERIALIZED 和 NOT MATERIALIZED 怎么选?
面试怎么回答
CTE 是查询组织方式,不一定物化。内联后优化器能跨 CTE 做谓词下推和 Join 重排;物化则能让昂贵结果只算一次,并形成优化边界,但会增加临时存储且可能挡住过滤。MATERIALIZED 和 NOT MATERIALIZED 的支持与默认行为依赖数据库版本,我会结合引用次数、函数副作用和 EXPLAIN 的实际扫描次数选择。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
很多人把 CTE 理解成“先生成一张临时表,再执行外层 SQL”。
这个说法有时成立,有时完全不成立。CTE 是语法结构,是否物化要看数据库版本、引用次数、递归与副作用,以及优化器决策。
CTE 不等于固定的临时结果;内联有利于谓词下推,物化能避免重复计算并形成优化边界,最终要用执行计划验证。
先把语法和执行方式分开
WITH 让复杂查询按逻辑块组织,也可以被后续语句引用。但“写成 CTE”只描述查询表达方式,并不天然规定必须落成临时结果。不同数据库、不同版本对 CTE 内联和物化的默认规则并不相同。
内联为什么可能更快
CTE 被合并进外层查询后,优化器可以跨边界做谓词下推、列裁剪和 Join 重排。比如外层只过滤一个小日期范围,内联后底层扫描可以直接裁掉无关分区;若先完整物化,再过滤,就可能多读大量数据。
物化也有它的价值
同一个昂贵 CTE 被多次引用时,先计算一次再复用,可能比每处重复执行更划算。物化还能保证含易变函数的结果只计算一次,并阻止某些错误或代价过高的跨层优化。但它会占用内存或临时磁盘,也可能挡住外层条件。

PostgreSQL 中如何显式控制
对无副作用、非递归的 WITH 查询,优化器在满足条件时可以折叠进父查询。MATERIALIZED 强制独立计算,NOT MATERIALIZED 倾向合并到父查询。后者可能让每个引用分别计算,所以不能机械认为 NOT MATERIALIZED 一定更快。
跨引擎不要照搬规则
Hive、Spark SQL、PostgreSQL、SQL Server 对 CTE 的实现与重用方式不同,有的更接近语法展开,有的可能插入复用或物化节点。面试回答要先说“依赖引擎和版本”,再说明内联与物化的成本模型,避免把某个数据库的行为当成 SQL 标准。
判断方法只有一个:看计划
用 EXPLAIN 或 EXPLAIN ANALYZE 看底层表被扫描几次、外层谓词是否下推、是否出现 CTE Scan、临时结果和 Spill。若 CTE 只是为了可读性,不要先入为主优化;只有发现重复计算或优化边界阻塞,再尝试改写或显式指定。
参考资料
- PostgreSQL WITH Queries:https://www.postgresql.org/docs/current/queries-with.html