oracle 19c 不支持物化视图中含子查询的快速刷新,因其破坏行级变更映射能力,导致刷新引擎无法关联基表日志与子查询结果;须改用带rowid别名和完整日志的join替代。
不支持。oracle 19c 的快速刷新(refresh fast)明确禁止物化视图定义中出现任何子查询。
为什么子查询会导致快速刷新失效
Oracle 快速刷新依赖确定性、可重写的增量变更应用,而子查询破坏了这个前提: - 子查询无法被物化视图刷新引擎解析为行级变更映射 - 即使是标量子查询(如(SELECT COUNT(*) FROM t2 WHERE t2.id = t1.id)),也会让 DBMS_MVIEW.EXPLAIN_MVIEW 返回 fastrefreshable = 'F' 或直接报错 ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view
- 刷新引擎无法将子查询结果与基表日志中的 ROWID 或主键变更关联,也就无法做增量计算
常见错误写法:
CREATE MATERIALIZED VIEW mv_with_subq AS
SELECT id, name,
(SELECT MAX(log_time) FROM audit_log a WHERE a.obj_id = t.id) last_audit
FROM objects t;
哪怕 audit_log 有完整日志,这个物化视图也只支持 COMPLETE 刷新。
替代方案:用 JOIN 替代子查询
只要逻辑等价,且满足快速刷新其他条件,JOIN 是安全的: - 把子查询改写为显式LEFT JOIN 或 INNER JOIN
- 对 JOIN 涉及的所有基表,都必须建好含 WITH PRIMARY KEY、INCLUDING NEW VALUES 和完整 SEQUENCE 的日志
- SELECT 列中必须包含每个基表的 ROWID 别名(如 t.ROWID t_rid, a.ROWID a_rid)
例如上面的标量子查询可改写为:
CREATE MATERIALIZED VIEW mv_with_join AS
SELECT t.id, t.name, a.max_log_time last_audit
FROM objects t
LEFT JOIN (
SELECT obj_id, MAX(log_time) max_log_time
FROM audit_log
GROUP BY obj_id
) a ON t.id = a.obj_id;
但注意:这个写法仍需确保 audit_log 日志包含 obj_id 和 log_time,且 objects 日志包含 id 和 name —— 缺一不可。
容易被忽略的兼容性陷阱
- 外连接(LEFT/RIGHT JOIN)本身在 Oracle 19c 中支持快速刷新,但前提是:
- 连接列上有唯一约束(如
audit_log.obj_id是外键或唯一索引) - 物化视图定义不含
SELECT *、SYSDATE、分析函数等其他禁用项 -
DBMS_MVIEW.EXPLAIN_MVIEW输出中没有"cannot fast refresh"类提示
WHERE 中(如 WHERE t.id IN (SELECT id FROM temp)),同样不支持;必须展开为 EXISTS + JOIN,且 temp 表也要建日志
真正难的不是“怎么写”,而是“每张基表的日志字段是否对齐物化视图里每一列的来源”。子查询绕开了这种显式依赖,所以被彻底禁止。











