分区表上物化视图支持查询重写需四者协同:显式启用enable query rewrite、query_rewrite_integrity设为stale_tolerated或trusted、mv定义保留原始分区键避免函数转换、手动创建匹配查询模式的复合索引。

分区表上的物化视图要支持查询重写(即“强制改写”),关键不在分区本身,而在于物化视图定义、重写参数、索引和完整性约束四者的协同——缺一不可。
物化视图必须显式启用 QUERY REWRITE
即使基表是分区表,只要创建时没加 ENABLE QUERY REWRITE,优化器就根本不会考虑重写。这不是后期能补的配置,必须在 CREATE MATERIALIZED VIEW 语句里声明:
- 错误写法:
CREATE MATERIALIZED VIEW mv_sales_by_month AS SELECT ...(无重写声明) - 正确写法:
CREATE MATERIALIZED VIEW mv_sales_by_month ENABLE QUERY REWRITE AS SELECT ... - 如果已存在但没启用,不能 ALTER 修改,只能
DROP MATERIALIZED VIEW后重建
QUERY_REWRITE_INTEGRITY 必须设为 STALE_TOLERATED 或 TRUSTED
分区表上建的物化视图,尤其是按时间范围分区的历史汇总 MV,常因刷新延迟被 Oracle 标记为 “stale”。此时若 QUERY_REWRITE_INTEGRITY = ENFORCED(默认值之一),重写会静默失败——哪怕 MV 状态是 VALID,EXPLAIN PLAN 也看不到重写路径。
- 会话级临时生效:
ALTER SESSION SET query_rewrite_integrity = STALE_TOLERATED; - 注意:该参数不能设为
FORCE来绕过检查,FORCE只影响成本评估,不改变 stale 判定逻辑 - 验证是否生效:
SHOW PARAMETER query_rewrite_integrity
物化视图定义需匹配高频查询的分区裁剪模式
Oracle 查询重写器不会自动“猜”你想要按分区过滤。如果报表 SQL 带 WHERE time_id >= DATE '2026-01-01',而 MV 定义里用了 TRUNC(time_id, 'MM') 或 EXTRACT(YEAR FROM time_id),重写可能失败——因为谓词无法精确映射到 MV 的列结构。
- 推荐做法:MV 中保留原始分区键(如
time_id),不在 SELECT 列中做函数转换 - 避免:
SELECT TRUNC(time_id, 'MM') AS month_key, ...→ 会阻断重写 - 允许:
SELECT time_id, region, SUM(sales) FROM sales PARTITION BY RANGE (time_id) GROUP BY time_id, region - 分区表上建 MV 时,加上
PARTITION BY RANGE (time_id)不是必须,但能提升后续REFRESH_FAST_AFTER_INSERT效率
必须手动建复合索引,且顺序要紧贴 WHERE + ORDER BY 模式
MV 是物理表,没有索引就等于裸表扫描。分区表基表上的索引对 MV 完全无效。
- 高频查询是
WHERE region = 'APAC' AND time_id BETWEEN ... ORDER BY time_id?那就建:CREATE INDEX idx_mv_region_time ON mv_sales_by_month (region, time_id) - 别建反了顺序(比如
(time_id, region)),否则等值过滤region时无法高效定位 - 索引必须在 MV 创建后立即建,不要等“报表变慢了再补”——重写是否启用,和执行快慢无关;但重写后查得慢,用户就会关掉
query_rewrite_enabled
最易被忽略的一点:即使所有配置都对,DBMS_MVIEW.EXPLAIN_REWRITE 才是唯一可信的诊断手段。别信 EXPLAIN PLAN 里有没有 MV 表名,那只是优化器“想不想”走重写;EXPLAIN_REWRITE 返回 TEXT_MATCH 或 GENERAL,才算真正打通了改写链路。











