普通视图不存储数据,仅保存SELECT语句;若定义含DISTINCT、UNION、GROUP BY等结构,优化器将物化中间结果,导致基表索引失效,GROUP BY被迫使用临时表排序。

视图定义触发物化,索引路径直接断掉
MySQL 和 PostgreSQL 的普通视图不存数据,只是保存 SELECT 语句的“快捷方式”。一旦视图定义里含 DISTINCT、UNION、GROUP BY、ORDER BY + LIMIT、子查询(如 (SELECT ...) AS v)或窗口函数,优化器大概率放弃内联展开,转而先执行视图逻辑、生成临时表(Materialize 或 Temp Table)。此时你对视图做 GROUP BY,实际是在对内存/磁盘里的临时结果分组——基表索引物理顺序彻底丢失。
验证方法:在 MySQL 8.0+ 中执行 EXPLAIN FORMAT=TREE SELECT ... FROM your_view GROUP BY ...,看到 Materialize 节点;PostgreSQL 中用 EXPLAIN (VERBOSE),出现 Subquery Scan 或 Materialize 即可确认。
即使视图可内联,GROUP BY 字段顺序也必须严丝合缝
假设基表有聚簇索引 (a, b, c),视图定义是 SELECT a, b, COUNT(*) FROM t GROUP BY a, b,且被成功内联——这时才可能走松散索引扫描(Using index for group-by)。但以下任一情况都会让索引失效:
-
GROUP BY b, a:字段顺序与索引最左前缀不一致 -
WHERE c > '2024':范围条件中断最左前缀,c后的索引字段无法利用 -
SELECT a, b, MAX(d):d不在索引中,触发回表,可能连带让整个GROUP BY放弃索引
SQL Server 的索引视图是特例,但限制极多
只有 SQL Server 支持真正给视图建聚簇索引,但必须同时满足:
- 创建视图时指定
SCHEMABINDING - 所有引用对象用两段式名(如
dbo.table) - 先建唯一聚集索引,且结果集行必须唯一(常靠
UNIQUE NOT NULL列组合保证) - 查询要命中该索引视图,还必须开启 5 个会话选项,例如
SET NUMERIC_ROUNDABORT OFF
MySQL 和 PostgreSQL 完全不支持这种机制;它们的物化视图(如 PostgreSQL 的 CREATE MATERIALIZED VIEW)是独立语法,和普通视图无关,也不自动继承基表索引。
你以为在查视图,其实优化器早把它重写成嵌套子查询
这是最容易被忽略的一点:你写的 SELECT * FROM v GROUP BY x,优化器可能早已重写为 SELECT * FROM (SELECT ... FROM t ...) AS v GROUP BY x。此时你面对的不是基表索引,而是中间结果集——它没有聚簇顺序,也没有你建的任何索引。哪怕基表索引再完美,只要视图定义破坏了内联可能性,GROUP BY 就只能走 Using temporary。

















