Oracle物化视图不支持LOCAL索引,仅支持GLOBAL索引;因其本质为非分区堆表或IOT,而LOCAL是分区表索引专属属性,故CREATE INDEX ... LOCAL必报ORA-02158错误。

Oracle物化视图不支持LOCAL索引——直接执行CREATE INDEX ... LOCAL会报ORA-02158错误,所谓“物化视图本地索引”是常见误解。
为什么CREATE INDEX ... LOCAL在物化视图上必然失败
物化视图默认是普通堆表(或IOT),没有分区段结构;LOCAL是分区表索引的专属属性,不能强加给非分区对象。即使基表是分区表、物化视图刷新语句里带ENABLE QUERY REWRITE,也不改变其物理形态。你试图运行:
CREATE INDEX mv_idx ON my_mv(col) LOCAL;
Oracle会立刻返回ORA-02158: invalid CREATE INDEX option。这不是权限或语法问题,是内核层面的硬性限制。
常见误操作包括:
- 把基表分区策略误认为自动继承到物化视图
- 在
DBA_INDEXES里看到LOCALITY = 'LOCAL'——那其实是查错了视图(该字段对非分区索引无意义) - 用
ON PREBUILT TABLE方式手工绑定已存在的分区表,但未严格匹配分区定义、约束和统计信息,导致物化视图元数据损坏
真正可用的只有GLOBAL索引,但需配合分区裁剪才有效
物化视图上能建的索引只有GLOBAL类型,包括:
- 不分区的
GLOBAL索引:CREATE INDEX mv_idx ON my_mv(col); - 分区的
GLOBAL索引:CREATE INDEX mv_idx ON my_mv(col) GLOBAL PARTITION BY RANGE(col) ...
但要注意:仅建索引不等于查询就能走索引。必须同时满足:
- 物化视图本身是显式分区创建的(
PARTITION BY RANGE(sales_month)),且版本≥21c(19c需打特定补丁并启用隐藏参数) - 查询谓词精确匹配分区键,如
WHERE sales_month = DATE '2026-08-01',才能触发分区裁剪 - 索引列包含分区键(如
(customer_id, sales_month)),否则优化器无法下推裁剪条件 -
DBA_INDEXES.STATUS为VALID,且DBA_TAB_STATISTICS.LAST_ANALYZED在刷新后已更新(否则优化器可能放弃索引)
分区物化视图 + GLOBAL索引的典型失效场景
即使你按规范建了分区物化视图和GLOBAL索引,以下情况仍会导致索引不被使用:
- 查询中对索引列用了函数:
WHERE UPPER(name) = 'ABC'→ 索引失效 - 谓词未覆盖分区键:
WHERE customer_id = 123但没带sales_month→ 无法裁剪,全分区扫描+索引回表成本过高,优化器选全表扫描 - 统计信息陈旧:
DBMS_STATS.GATHER_TABLE_STATS未设cascade => TRUE,索引统计缺失 - 物化视图刷新后未
ANALYZE或未收集直方图,导致选择性误判
此时看EXPLAIN PLAN,OBJECT_PARTITION列为空,OPERATION是TABLE ACCESS FULL,而非INDEX RANGE SCAN + PARTITION LIST SINGLE。
别被“本地效果”误导,重点检查三个真实状态
当发现查询性能差,不要纠结“为什么不是LOCAL”,而是立刻验证这三项:
-
SELECT STATUS FROM DBA_INDEXES WHERE INDEX_NAME = 'MV_IDX'—— 必须是VALID,UNUSABLE需ALTER INDEX MV_IDX REBUILD -
SELECT LAST_ANALYZED FROM DBA_TAB_STATISTICS WHERE TABLE_NAME = 'MY_MV' AND OBJECT_TYPE = 'TABLE'—— 刷新后必须重新收集 -
SELECT COLUMN_NAME, NUM_DISTINCT FROM DBA_TAB_COL_STATISTICS WHERE TABLE_NAME = 'MY_MV' AND COLUMN_NAME IN ('SALES_MONTH', 'CUSTOMER_ID')—— 确认关键列有有效统计
物化视图的索引行为本质是“分区裁剪 + 全局索引”的组合效果,不是真正的本地索引。混淆这点,排查方向就全错了。











