物化视图无法FAST REFRESH主因是物化视图日志未正确创建:需显式指定PRIMARY KEY、覆盖MV中所有参与聚合/过滤/连接的列、INCLUDING NEW VALUES及SEQUENCE,否则自动退化为COMPLETE刷新。

物化视图无法 FAST REFRESH,90% 是因为物化视图日志没建对,而不是 MV 本身写错了。
物化视图日志必须显式创建,Oracle 不会自动补上
很多人以为只要写了 REFRESH FAST,Oracle 就会“智能”帮你建日志。事实是:日志完全不会自动生成,漏掉这步,后续所有刷新都会退化为 COMPLETE,且不报错——只在 DBA_MVIEW_ANALYSIS 里悄悄记下 increfreshtim 为 0 或空。
创建日志前必须确认:
- 源表已有主键或唯一约束(
WITH PRIMARY KEY方式依赖它) - 日志中列出的列,必须覆盖 MV 查询中所有参与聚合、过滤、连接的列(比如 MV 里用了
SUM(pay_amount)和WHERE pay_status = 1,日志就必须包含pay_amount和pay_status) - 必须指定
INCLUDING NEW VALUES,否则 UPDATE 操作无法被正确拆解为旧值减、新值加
典型错误命令:CREATE MATERIALIZED VIEW LOG ON t_order; —— 这条语句只建了个空壳日志,不含任何列、不含 SEQUENCE$$、不记录新值,FAST 刷新必然失败。
日志字段必须和 MV 的 SELECT 列严格对齐
不是“源表有啥列就全塞进去”,而是要按 MV 定义反推需要哪些列。例如这个 MV:
CREATE MATERIALIZED VIEW mv_daily_sales
REFRESH FAST ON DEMAND
AS
SELECT TRUNC(create_time) AS stat_date,
COUNT(*) AS order_count,
SUM(pay_amount) AS total_amount
FROM t_order
WHERE pay_status = 1
GROUP BY TRUNC(create_time);它实际依赖的底层字段是:create_time(用于 TRUNC() 分组)、pay_amount(用于 SUM)、pay_status(用于 WHERE 过滤)。日志就必须显式包含这三列:
CREATE MATERIALIZED VIEW LOG ON t_order WITH PRIMARY KEY, SEQUENCE(create_time, pay_amount, pay_status) INCLUDING NEW VALUES;
常见坑:
- 漏掉
pay_status→ WHERE 条件变更无法识别,UPDATE/DELETE 可能被跳过 - 用
ROWID替代PRIMARY KEY→ 如果 MV 含聚合,Oracle 要求必须用主键方式,否则拒绝 FAST - 列名大小写或别名不一致 → 日志列名是原始列名,不是 MV 中的别名,
TRUNC(create_time)对应的日志列只能是create_time
刷新时 MV 自动读日志,但顺序和完整性靠 SEQUENCE$$ 保证
执行 DBMS_MVIEW.REFRESH('mv_daily_sales', 'F') 时,Oracle 并不是简单地扫一遍 MLOG$_T_ORDER 表。它会:
- 先查
SNAPTIME$$字段,只处理上次刷新后新增的记录(值为01-JAN-4000的行) - 按
SEQUENCE$$升序应用每条变更,确保事务顺序不乱(比如先 INSERT 再 UPDATE 同一行,不能颠倒) - 对
DMLTYPE$$ = 'U'的记录,结合OLD_NEW$$区分旧值('O')和新值('N'),分别做减法和加法
所以,如果日志里没有 SEQUENCE,或者没设 INCLUDING NEW VALUES,Oracle 就无法安全还原 UPDATE 的语义,只能放弃 FAST,回退到 COMPLETE。
验证日志是否生效,别只看创建成功
建完日志和 MV 后,务必手动触发一次 DML + 刷新,并检查结果是否符合预期:
- 对源表做一条
INSERT,再执行EXEC DBMS_MVIEW.REFRESH('mv_daily_sales', 'F'),MV 行数应 +1,聚合值应更新 - 再
UPDATE一条已存在的记录(如改pay_status),刷新后 MV 应体现净变化(比如从计入变不计入,总数 -1) - 查
SELECT * FROM MLOG$_T_ORDER,确认有对应记录,且DMLTYPE$$、OLD_NEW$$、SEQUENCE$$值合理
最容易被忽略的是:日志建好后,如果后续修改了源表结构(比如新增一列并用于 MV 查询),但忘了同步更新日志定义,FAST 刷新就会静默失败。每次变更基表或 MV 定义,都要重新用 DBMS_MVIEW.EXPLAIN_MVIEW 检查兼容性。


















