结论:视图适用于跨会话、多次复用的查询逻辑,cte适用于单条sql内拆解复杂逻辑、提升可读性;cte非物化,重复引用会重新执行,并非缓存。

视图 vs CTE:什么时候该用哪个
直接说结论:如果查询逻辑要被多次、跨会话复用,选视图;如果只是当前 SQL 内部拆解复杂逻辑、提升可读性,用 CTE。两者根本不是替代关系,而是生命周期和作用域不同。
CTE 重复引用会重新执行,不是“缓存”
很多人误以为 WITH 定义一次就能“复用结果”,其实 CTE 默认不物化——每次在主查询中被引用,优化器都可能重新执行其内部 SELECT。比如:
WITH user_stats AS ( SELECT user_id, COUNT(*) cnt FROM orders GROUP BY user_id ) SELECT * FROM user_stats WHERE cnt > 10 UNION ALL SELECT * FROM user_stats WHERE cnt <p>上面的 <code>user_stats</code> 在 <code>UNION ALL</code> 两侧各执行一次,底层表扫描和聚合也发生两次。这不是 bug,是标准行为。</p>
- MySQL 8.0+ 和 PostgreSQL 可通过
MATERIALIZED(PostgreSQL)或优化器开关(如optimizer_switch='derived_merge=off')强制物化,但需显式控制 - SQL Server 默认尝试合并(MERGE),除非 CTE 被引用多次或含不支持合并的结构(如聚合 + TOP),才可能物化
- 若真需要复用计算结果,不如改用
CREATE TEMP TABLE AS SELECT ...
视图不能下推谓词?先看是否定义了可下推的结构
SELECT * FROM my_view WHERE status = 'done' 慢,常见原因不是“视图天生慢”,而是视图定义里用了不可下推的表达式或嵌套:
- 含
ROW_NUMBER()、GROUP BY后再HAVING的视图,外层WHERE很难下推到基表 - 定义里用了
CONVERT(...)或函数包装列(如UPPER(name)),导致索引失效且无法下推 - 多层视图嵌套(A → B → C)会让优化器放弃重写,退化为嵌套子查询
- 解决办法:检查
EXPLAIN输出,确认谓词是否落在最内层扫描上;必要时把关键过滤提前到视图定义里
权限和部署场景决定能否用视图
CTE 是纯语法糖,不涉及元数据或权限系统;视图则是一个数据库对象,有独立的 GRANT/REVOKE 控制能力:
- 想让业务用户只能查脱敏字段?必须用视图,CTE 做不到权限隔离
- 视图可被其他视图、存储过程、应用代码反复调用,适合封装稳定接口
- 但视图一旦创建就长期存在,命名冲突、依赖混乱、
pg_views元数据膨胀都是真实问题,尤其在团队协作频繁的 PostgreSQL 环境 - 临时需求、调试阶段、一次性报表——别建视图,用 CTE 或临时表更轻量
真正容易被忽略的点是:CTE 和视图都解决不了“中间结果加索引”的问题。需要索引加速后续操作?只有临时表能建 INDEX,这是它们之间不可逾越的性能分水岭。










