视图不存数据,仅保存sql语句,执行时展开并经优化器处理,若where条件列无索引、含函数、or/is null/not in等,或join驱动表选择不当,均导致全表扫描;须用explain验证展开后sql及索引使用情况。

视图定义里WHERE条件没索引,照样全表扫描
视图本身不存数据,只是保存SQL语句的“快捷方式”。执行SELECT * FROM my_view时,MySQL会把视图展开成原始SELECT,再走一遍优化器逻辑——该全表扫描还是全表扫描。
常见错误现象:视图里写了WHERE status = 'active',但底层表status列没索引;或者写了WHERE DATE(created_at) = '2024-01-01',哪怕created_at有索引也失效。
- 先确认视图展开后的实际SQL:用
SHOW CREATE VIEW my_view看定义,复制出来手动加EXPLAIN验证 - 检查底层表对应字段是否真有索引:
SHOW INDEX FROM base_table,别只看名字猜 - 避免在视图定义中对索引列用函数、表达式或隐式转换,比如
UPPER(name)、mobile + ''、id != 1
联合查询视图+JOIN时驱动表选错,索引白建
视图常被用在多表JOIN中,比如SELECT * FROM user_view u JOIN orders o ON u.id = o.user_id。这时优化器可能把视图展开后的结果当驱动表,而它没索引、行数又大,导致orders表每行都触发回表查找。
- 用
EXPLAIN FORMAT=TREE(MySQL 8.0+)看实际连接顺序,确认谁是outer table - 如果视图展开后是大结果集,优先把它拆出来物化(如用CTE或临时表),再JOIN
- 确保JOIN条件列在被驱动表上有索引:
orders.user_id必须有索引,且不能是user_id + status这种复合索引里非最左列 - 避免在ON里写函数:
ON u.id = CAST(o.user_id AS SIGNED)会让o.user_id索引失效
视图含OR/IS NULL/NOT IN,优化器直接放弃索引
视图定义若包含WHERE a = 1 OR b = 2或WHERE col IS NULL,即使a、b、col各自有索引,优化器也大概率跳过索引走ALL——因为成本估算认为扫一遍更快。
-
OR条件尽量拆成UNION ALL子查询,前提是各分支能独立走索引 -
IS NULL改用默认值替代:比如col设默认0,查WHERE col = 0;或加NOT NULL约束后重建索引 -
NOT IN (1, 2, NULL)右侧含NULL会导致整个条件恒假,且强制全表扫描;改用NOT EXISTS或排除NULL后再NOT IN - 视图里慎用
IN列表,尤其当值数量波动大时;连续范围优先用BETWEEN
存储过程调用视图时参数嗅探让执行计划固化
存储过程中SELECT * FROM my_view WHERE user_id = @uid,第一次执行用@uid = 1(返回10行),优化器生成了走索引的计划并缓存;后续用@uid = 999999(返回50万行),仍复用旧计划,结果慢得离谱。
- 用
EXPLAIN带实际参数值测试,别只看存储过程里的变量名 - 临时解法:加
FORCE INDEX,但仅限单表视图;复合索引必须字段顺序匹配WHERE条件顺序 - 更稳做法:把参数提前算成确定范围,比如
WHERE login_time BETWEEN @start AND @end,并确保索引最左列是login_time或user_id - 定期运行
ANALYZE TABLE base_table,尤其在大批量导入/删除后,否则统计信息过期,优化器总误判
视图不是性能银弹,它掩盖不了底层SQL的缺陷。真正关键的是:每次修改视图定义后,必须用EXPLAIN验证真实执行路径,而不是相信“我建了索引,它就应该走”。











