QUERY_REWRITE_ENABLED必须为TRUE,物化视图才进入重写候选;需确保会话级未禁用、MV创建时启用QUERY REWRITE、无不可重写构造、状态非STALE、约束有效、日志字段完整、列与表达式严格等价、分区键裸用且名一致。

QUERY_REWRITE_ENABLED 必须为 TRUE,否则物化视图根本不会进入重写候选列表——这是最常被忽略的硬性开关,不是“默认就开”的可选项。
为什么执行计划里完全看不到物化视图名
这不是优化器“选错了”,而是压根没把它当备选项。常见原因包括:
-
QUERY_REWRITE_ENABLED在会话级被显式设为FALSE(例如执行过ALTER SESSION SET QUERY_REWRITE_ENABLED = FALSE),会覆盖系统级设置 - 物化视图创建时没加
ENABLE QUERY REWRITE关键字,即使参数开了也没用 - 查询语句用了不可重写的构造:如
SYSDATE、ROWNUM、DBMS_RANDOM.VALUE()、子查询含非确定性函数 - 物化视图状态为
STALE,而QUERY_REWRITE_INTEGRITY是默认的ENFORCED模式,此时直接跳过
DBMS_MVIEW.EXPLAIN_REWRITE 显示 “no match” 或 “integrity violation”
这说明语法和语义校验失败,不是成本问题。重点排查:
- 基表缺少已验证(
VALIDATED)的主键或外键约束——ENFORCED模式下这是强制要求 - 物化视图日志缺失或字段不全:比如物化视图 SELECT 中用了
product_id, category,但日志只建了WITH ROWID,没写SEQUENCE (product_id, category) - 查询中的列别名或表达式与物化视图定义不严格等价:例如 MV 里是
UPPER(name),查询写的是name = 'ABC',即使结果一致,也不匹配 - 分区键表达式不一致:MV 按
TRUNC(sale_date)分区,查询却用sale_date >= DATE '2024-01-01',Oracle 无法推导等价性
执行计划显示基表,但 EXPLAIN_REWRITE 说“can be rewritten”
说明重写逻辑通过了,但 CBO 成本估算让它输了。典型诱因:
- 物化视图本身没收集统计信息:
DBMS_STATS.GATHER_TABLE_STATS必须显式执行,且对分区 MV 要加GRANULARITY => 'ALL' - 基表统计信息陈旧,导致 CBO 高估基表扫描成本、低估 MV 访问成本
- 物化视图上没建索引:它是一张物理表,但默认无任何索引,
WHERE month = '2024-03'很可能触发全表扫描 - 查询中用了 hint 如
/*+ FULL(t) */,或设置了OPTIMIZER_MODE = RULE(已弃用但仍有遗留)
分区物化视图没走分区裁剪,PARTITION_START 显示 KEY
关键点在于:分区裁剪发生在物化视图被访问之后,而重写失败则根本不会访问它。所以先确保重写发生,再谈裁剪。
- 确认查询过滤的列名与 MV DDL 中的分区键列名**完全一致**(大小写、是否带双引号)
- 禁止在 WHERE 中对分区键列做任何包装:
NVL(dt, sysdate)、TO_CHAR(dt)、dt + 0全部失效 - 用
EXPLAIN PLAN FOR SELECT ...后查V$SQL_PLAN,看OBJECT_NAME是否为 MV 名,且PARTITION_START/PARTITION_STOP是数字而非KEY - 临时加
/*+ REWRITE(mv_name) */测试,若计划立刻切过去,就坐实是 CBO 成本误判,不是语义问题
真正卡住的地方往往不是“怎么建”,而是“为什么不用”。重写机制高度依赖语义精确性、元数据完整性与统计信息时效性,三者缺一不可。最容易被绕过的其实是物化视图日志的字段覆盖和分区键的裸列使用——这两处一旦出错,连诊断消息都可能只报模糊的 QSM-01150。










