物化视图本身不自动避免全表扫描,其查询是否走索引取决于是否建立合适索引、统计信息是否及时更新、查询谓词是否支持分区剪枝及索引使用,三者缺一不可。
物化视图本身不自动避免全表扫描
物化视图(materialized view)只是预计算并存储结果集的“快照”,它不会天然绕过全表扫描。如果查询物化视图时没有合适的访问路径,oracle 仍可能对物化视图基表(即物化视图对应的底层存储段)执行全表扫描——尤其当查询条件未命中物化视图上的索引,或优化器判定扫描比索引查找更便宜时。
关键点在于:物化视图需要像普通表一样被索引、分区和统计信息维护,否则它和一张大表没区别。
- 物化视图默认不带索引,必须显式创建
- 物化视图的刷新日志(
LOG)和主键约束会影响能否使用快速刷新,但和查询时是否走索引无关 - 即使物化视图已建索引,若查询中对索引列用了
UPPER()、TO_CHAR()或IS NULL,索引依然失效
在物化视图上创建分区索引前先分区物化视图
Oracle 不允许直接对非分区物化视图创建分区索引。你必须先让物化视图本身支持分区(即定义为分区物化视图),然后才能在其上建本地分区索引(LOCAL)。
常见错误是试图对普通物化视图执行:CREATE INDEX idx_mv ON mv_name(col) LOCAL —— 这会报错 ORA-14035: invalid partition name。
- 创建分区物化视图需在
CREATE MATERIALIZED VIEW语句中显式指定分区子句,例如按日期范围:PARTITION BY RANGE (log_date) - 分区键必须是物化视图 SELECT 列表中的列,且不能是表达式或函数结果
- 本地索引(
LOCAL)会随物化视图分区自动对齐;全局索引(GLOBAL)可跨分区,但 DML 维护成本高,刷新时易失效
物化视图 + 分区 + 索引后仍被全表扫描?检查三个硬性前提
即使你完成了分区和索引,执行计划里还是出现 FULL TABLE SCAN,大概率卡在这三处:
-
DBMS_STATS.GATHER_TABLE_STATS没跑过:物化视图作为段对象,必须收集统计信息,否则优化器无法估算分区剪枝效果,宁愿全扫也不信“只查一个分区” - 查询谓词未触发分区剪枝:比如物化视图按
sale_date分区,但查询写的是WHERE TO_CHAR(sale_date, 'YYYYMM') = '202512'—— 函数导致分区键不可用,剪枝失败 - 物化视图未启用查询重写(
QUERY REWRITE):若原始 SQL 查的是基表,而你期望它自动路由到物化视图,必须确保物化视图建在ENABLE QUERY REWRITE模式下,且会话级开启ALTER SESSION SET QUERY_REWRITE_ENABLED = TRUE
为什么物化视图上建普通 B-Tree 索引有时比分区索引更有效
不是所有场景都适合分区索引。当物化视图数据量不大(比如百万级以下)、查询高度集中在少数几个值(如状态码、类型 ID),一个紧凑的全局 B-Tree 索引反而比本地分区索引更快——因为免去了分区定位开销,且叶块更连续。
典型例子:CREATE INDEX idx_mv_status ON mv_orders(status) NOLOGGING,配合 WHERE status IN ('SHIPPED', 'CANCELLED'),性能常优于按时间分区再加本地索引。
- 本地索引优势在超大物化视图(TB 级)+ 时间范围查询(如最近 7 天)
- B-Tree 全局索引在等值查询、IN 列表、小范围范围查询中响应更稳定
- 注意:全局索引在物化视图刷新(尤其是
FAST刷新)期间可能被标记为UNUSABLE,需额外REBUILD
UPPER() 或绑定变量未绑定到位,优化器就可能放弃所有索引路径。


















