union all视图变慢的根本原因是子查询执行效率低或中间结果膨胀。视图不缓存结果,每次查询都重执行全部子查询;若子查询未走索引、返回冗余字段、数据量大,会触发临时表和磁盘排序,尤其外层加order by或limit时需物化全部结果。

UNION ALL 视图本身不缓存结果,性能瓶颈几乎总是出在子查询执行效率或中间结果膨胀上。
为什么UNION ALL视图会变慢
视图只是保存的SQL定义,每次查询它,数据库都会重写并执行整个UNION ALL结构。如果每个子查询没走索引、返回大量列或数据量大,合并过程就会触发临时表(Using temporary)甚至磁盘排序(Using filesort)。尤其当外层再加ORDER BY或LIMIT时,数据库必须先把所有子查询结果拉出来才能排序——哪怕你只想要前10行。
- 子查询未加
WHERE条件,导致全表扫描 - 用了
SELECT *,传输和处理冗余字段(如TEXT、JSON列) - 各子查询结果集大小差异极大,小查询被大查询拖慢
- MySQL临时表超过
tmp_table_size,被迫写磁盘
每个子查询必须独立可优化
数据库不会跨UNION ALL子句做全局优化。你得把每个SELECT当成独立查询来调优:
- 对每个子查询单独跑
EXPLAIN,确认是否命中索引(特别是type为ref或range,而非ALL) - 给
WHERE、JOIN、ORDER BY涉及的列建覆盖索引,避免回表 - 把过滤条件明确写进每个子查询内部,而不是只在外层加
WHERE——例如用(SELECT id FROM t1 WHERE status = 'active') UNION ALL (SELECT id FROM t2 WHERE status = 'pending'),而不是(SELECT id FROM t1) UNION ALL (SELECT id FROM t2) WHERE status IN ('active', 'pending') - 若某子查询只用于补缺(如默认值),可用
SELECT 1 AS id, 'N/A' AS name LIMIT 0占位,避免无意义扫描
避免在视图里做重排序或分页
视图定义中包含ORDER BY在多数数据库(如MySQL)中是非法的;即使允许(如PostgreSQL),它也只影响视图自身输出顺序,无法加速外部查询。真正危险的是在视图外再套ORDER BY ... LIMIT:
- 数据库必须物化全部
UNION ALL结果后才排序,内存压力陡增 - 改用“子查询内部分页”:比如按时间分片的表,每个子查询加
LIMIT 1000,再在外层统一排序取前N条 - 更稳妥的做法是放弃视图封装,改用应用层拼接SQL,或用CTE +
ROW_NUMBER()控制去重/截断逻辑 - 如果视图被高频调用且结构稳定,考虑用物化方式替代——例如定时把结果写入一张
summary_union表,加索引后直接查它
类型隐式转换会悄悄拖垮性能
两个子查询对应列类型不一致时,MySQL可能自动做隐式转换(如把VARCHAR(50)转成VARCHAR(255)),这会导致索引失效、比较变慢,甚至报错:
- 显式用
CAST()或CONVERT()统一类型,例如CAST(created_at AS DATE)、CAST(id AS BIGINT) - 避免用
NULL占位却不声明类型:SELECT id, name FROM t1 UNION ALL SELECT id, NULL FROM t2中,NULL类型由上下文推断,易出错;应写成SELECT id, name FROM t1 UNION ALL SELECT id, CAST(NULL AS VARCHAR(100)) FROM t2 - 检查
SHOW CREATE VIEW view_name输出,确认每列实际类型是否符合预期
最常被忽略的一点:UNION ALL 视图的“快”,只在子查询本身足够轻量时成立。一旦某个分支开始扫全表、读大字段或关联复杂表,整个视图就失去意义——这时候不是优化视图,而是该重构数据访问路径了。











