Oracle 19c物化视图FAST刷新静默退化为COMPLETE,根本原因是日志缺失必要字段:必须显式创建含WITH PRIMARY KEY(或ROWID)、INCLUDING NEW VALUES及完整SEQUENCE列的日志,缺一即降级。
oracle 19c 中物化视图要支持 fast 刷新,核心不是“怎么写物化视图”,而是“日志建对没建全”——缺任何一个必要字段,refresh fast 就会静默退化为 complete,你只会发现刷新变慢,却看不到明确报错。
为什么 REFRESH FAST 会悄悄变成 COMPLETE
Oracle 不会在执行 REFRESH FAST 前校验所有条件是否满足,而是在运行时动态判断:只要任意一条硬性条件不成立,就自动降级。常见静默失败原因包括:
-
ORA-12052:某个基表的物化视图日志缺失WITH ROWID或未包含关键 JOIN 列 -
ORA-12004:日志建在了错误表上(比如物化视图查的是sales_part,但日志建在了sales_header) - 日志里没加
INCLUDING NEW VALUES:UPDATE 和 INSERT 操作无法被识别为增量,刷新引擎只能重跑全量 - 物化视图定义中用了
SYSDATE、ROWNUM或SELECT *:直接导致DBMS_MVIEW.EXPLAIN_MVIEW返回fastrefreshable = FALSE
创建物化视图日志必须带的三个参数
只写 CREATE MATERIALIZED VIEW LOG ON t 是无效的。Oracle 19c 要求日志显式声明行为边界,否则 FAST 刷新无法启动:
- 必须用
WITH ROWID或WITH PRIMARY KEY—— 推荐主键,因为分区表中ROWID可能随分裂/移动失效 - 必须加
INCLUDING NEW VALUES—— 否则日志只存旧值,INSERT 和 UPDATE 新值不可见 - 必须显式列出所有被物化视图引用的列(含 JOIN 条件列、WHERE 过滤列、聚合字段),例如:
SEQUENCE(sale_id, amount, status, sale_date);不能只写SEQUENCE(*),Oracle 不支持通配符
正确示例(针对分区表 sales_part):
CREATE MATERIALIZED VIEW LOG ON sales_part WITH PRIMARY KEY, SEQUENCE(sale_id, amount, status, sale_date) INCLUDING NEW VALUES;
多基表关联时,每个表的日志都要独立配置
物化视图如果 JOIN 了 orders 和 customers,那不是建一个日志就够了——两个基表都得有各自完整的日志,且结构对齐:
-
orders日志必须含customer_id(JOIN 列)和ROWID,并显式写进SEQUENCE() -
customers日志必须含id(JOIN 列)和ROWID,同样进SEQUENCE() - 物化视图 SQL 的
SELECT列表里,必须显式写出每个基表的ROWID别名,例如:o.ROWID o_rowid, c.ROWID c_rowid—— 它们不参与业务逻辑,但刷新引擎靠它们定位变更行 - 哪怕只有一个表的日志漏了
ROWID,整个物化视图都会退化为 COMPLETE 刷新,且无提示
分区表上不存在“按分区增量刷新”这回事
这是最容易被文档误导的一点:Oracle 19c 的 FAST 刷新永远是行级的,不是分区级的。日志表 MLOG$_sales_part 里没有 partition_name 字段,DMLTYPE$$ 和 ROWID 也不携带分区信息。所谓“只刷 P202604 分区”,实际是误把物理读取的并行扫描(Oracle 可以并发读多个分区)当成了逻辑上的分区刷新。
如果你真需要分区粒度控制,唯一可行路径是:
- 手动拆分物化视图:为每个分区建单独的物化视图(如
mv_sales_p202604),各自配独立日志和刷新作业 - 用
DBMS_MVIEW.REFRESH的list参数指定单个 MV 刷新,而非依赖全局机制 - 注意:这种做法会显著增加日志维护开销和管理复杂度,仅在极特殊场景下权衡使用
真正该盯住的,始终是日志字段是否完整、是否匹配物化视图定义中的每一列引用——这才是 FAST 刷新能否稳住的唯一支点。


















