query rewrite 未生效的首要原因是物化视图未显式启用重写能力(enable_query_rewrite='n'),且会话参数 query_rewrite_integrity 未设为 trusted;即使结构匹配、sql 完全一致,这两个开关未打开则重写必然失败。
query rewrite 为什么没生效?先看物化视图是否被标记为 enabled
oracle 不会自动启用 query rewrite,即使物化视图存在且结构匹配。核心前提是物化视图必须显式启用重写能力,否则优化器直接忽略它。
-
DBA_MVIEWS.ENABLE_QUERY_REWRITE字段必须为'Y',不是默认值(建表时默认是'N') - 启用方式:创建时加
ENABLE QUERY REWRITE,或后续用ALTER MATERIALIZED VIEW ... ENABLE QUERY REWRITE - 检查语句:
SELECT mview_name, enable_query_rewrite FROM dba_mviews WHERE mview_name = 'YOUR_MV_NAME';
- 如果返回
'N',重写一定不触发——哪怕 SQL 完全匹配、成本更低,也毫无作用
物化视图定义里有没有禁用重写的隐式陷阱?
某些看似无害的语法或函数会直接让 Oracle 判定该物化视图「不可用于重写」,即使它能成功刷新。
- 含
ROWNUM、SYSDATE、USER、UID、LEVEL等非确定性伪列 → 禁止重写 - 使用了未声明为
DETERMINISTIC的自定义函数 → 禁止重写 - 包含
FOR UPDATE、CONNECT BY(无NOCYCLE且无明确终止条件)→ 多数版本不支持重写 - 聚合物化视图中漏掉
GROUP BY所有非聚合列,或没包含COUNT(*)→ 可能导致重写被跳过(尤其涉及空值处理时)
怎么验证当前 SQL 是否真的被重写了?别只信执行计划里的 “MAT_VIEW REWRITE”
执行计划出现 MAT_VIEW REWRITE 并不绝对可靠——它可能只是“尝试重写”,实际仍访问基表。必须交叉验证。
- 开启重写诊断:
ALTER SESSION SET QUERY_REWRITE_INTEGRITY = TRUSTED;<br>ALTER SESSION SET "_query_rewrite_driven" = TRUE;
(后者在较新版本中影响判断逻辑) - 强制走重写并对比:在 SQL 前加
/*+ REWRITE */提示,再看执行计划是否真用了 MV 的扫描(如TABLE ACCESS FULL对应 MV 名) - 查重写日志:
SELECT * FROM V$REWRITE_EQUIVALENCE WHERE MV_OBJ# = (SELECT OBJECT_ID FROM DBA_OBJECTS WHERE OBJECT_NAME = 'YOUR_MV_NAME');
若为空,说明 Oracle 根本没把它纳入等价类 - 最直白办法:对 MV 和原 SQL 分别
EXPLAIN PLAN,比对OBJECT_NAME列是否变成 MV 名
QUERY_REWRITE_INTEGRITY 设置不当会导致“明明符合条件却不用”
这个参数控制优化器对数据一致性的容忍度,值不对,再合规的 MV 也会被绕过。
-
ENFORCED:只重写能数学证明等价的查询(最严,要求 MV 含完整键信息、无 NULL 敏感聚合等) -
TRUSTED:信任用户声明的约束和 MV 定义(常用,但需确保主外键、NOT NULL、CHECK 约束真实存在且已启用) -
STALE_TOLERATED:连 stale MV 都敢用——但若 MV 实际STALE,重写后结果可能错 - 检查当前会话设置:
SELECT SYS_CONTEXT('USERENV', 'QUERY_REWRITE_INTEGRITY') FROM DUAL; - 常见坑:建了 MV 却忘了在基表上
ENABLE VALIDATE CONSTRAINT主键/外键,TRUSTED模式下仍无法重写
物化视图能否被 QUERY REWRITE 使用,从来不是单点问题。它卡在权限、定义、约束、会话参数、甚至 MV 刷新状态多个环节上。最容易被忽略的是:ENABLE_QUERY_REWRITE = 'Y' 和 QUERY_REWRITE_INTEGRITY = 'TRUSTED' 这两个开关没打开,其余所有努力都白费。











