WHERE条件应优先写在视图内以确保下推至基表,若写在视图外则依赖优化器能否穿透——简单投影可下推,含函数、表达式、GROUP BY或LEFT JOIN中左表条件则失效,导致全表扫描。

WHERE 条件写在视图外还是视图内?关键看是否能穿透到基表
视图本身不存数据,所谓“下推”,就是让 WHERE 条件最终落到基表扫描节点上。如果视图定义里是简单投影(比如 SELECT id, name, location FROM places),那么外部查询 SELECT * FROM v_places WHERE location.STIntersects(@poly) = 1 才可能走空间索引;一旦视图里用了 STTransform() 或 UPPER(name),优化器就无法把条件映射回原始列,下推立即失效。
常见错误现象:执行计划中基表行显示 type = "Table Scan"、key = NULL,而 Filter 出现在最外层节点——这说明条件被当成了“后过滤”,不是“先筛选”。
- 验证方法:用
EXPLAIN (ANALYZE, BUFFERS)(PostgreSQL)或SET STATISTICS XML ON(SQL Server)对比视图查询与等价原始 SQL 的执行计划 - MySQL 用户注意:
ALGORITHM = TEMPTABLE会强制物化中间结果,彻底绕过基表索引,哪怕只加一个WHERE user_id = 123也无效 - 避免在视图字段中使用任何表达式:哪怕只是
col AS col都不算问题,但UPPER(col) AS col就会破坏下推能力
LEFT JOIN 视图里加 WHERE,为什么左表总被全扫?
LEFT JOIN 的语义决定了左表所有行必须保留,所以写在 WHERE 子句里的左表条件(如 WHERE u.status = 'active')几乎不可能下推——优化器不敢提前过滤,怕丢掉本该保留的 NULL 行。结果就是对 users 表做全表扫描,哪怕它有 status 索引。
真正能下推的,只有作用于右表(非保留表)的条件,比如 WHERE o.amount > 100,这个通常能落到 orders 表扫描阶段。
- 补救办法:把左表过滤提前,用 CTE 或子查询先筛出
u.status = 'active'的子集,再LEFT JOIN - 别指望
STRAIGHT_JOIN或FORCE INDEX能绕过——这是语义限制,不是优化器 bug - 检查执行计划:如果左表扫描的
rows预估接近其总行数,基本可断定没下推
嵌套视图 + GROUP BY,为什么执行计划总出现 Materialize?
含 GROUP BY、DISTINCT、UNION、窗口函数的视图,基本会触发物化路径。数据库无法保证外层 WHERE 条件语义等价地注入聚合前,所以选择先算完再过滤——执行计划里就会冒出 Materialize(PG)、Table Spool(SQL Server)或 Using temporary(MySQL)。
嵌套三层以上更危险:视图 A → B → C 展开后,优化器对中间结果集大小估算偏差极大,尤其某层用了 LIMIT 或 OFFSET,会导致驱动表选错、连接顺序混乱,甚至放弃所有外层谓词下推。
- 快速验证:用
pg_get_viewdef('v_name')(PG)或sp_helptext 'v_name'(SQL Server)展开所有层级,手动拼出最终 SQL 再跑一次EXPLAIN - 优先用单层视图;必须嵌套时,每层都要单独验证谓词是否下推到了对应基表扫描节点
- 不要依赖
SELECT *:基表加一列,视图结果就多一列,前端按位置取值(如res[2])立刻错位
字段类型或 COLLATION 不一致,为什么索引直接消失?
视图展开后,JOIN 条件两边字段类型不匹配(比如 users.id INT vs logs.user_id VARCHAR),或字符集/排序规则不同(如 utf8mb4_general_ci vs utf8mb4_0900_as_cs),优化器就无法用索引做等值查找——执行计划里直接显示 type = ALL、key = NULL。
这类问题在嵌套视图中极难发现,因为 SHOW CREATE VIEW 只显示当前层定义,查不到底层字段的真实定义。
- 验证方式:
SHOW FULL COLUMNS FROM table_name LIKE 'col_name'(MySQL)或\d+ table_name(PostgreSQL)比对字段完整定义 - 重点核对
Collation列是否完全一致;只改CHARACTER SET不同步COLLATE是无效的 - 避免在
ON子句里写t1.id = t2.id OR (t1.id IS NULL AND t2.id IS NULL)这类逻辑,既破坏索引,又干扰基数估算
复杂点不在“建不建视图”,而在“每一层展开后,WHERE 是否还落在物理列上”。容易被忽略的是:视图不校验依赖,一查就展开,出错就是基表或中间计算环节出了问题——你看到的慢,往往不是视图慢,而是它背后那条被强行物化的 SQL 慢。










