必须分三步:先通DB Link(需PUBLIC权限、网络连通、真实表测试)、再建源库物化视图日志(WITH PRIMARY KEY或ROWID)、最后建MV并显式指定REFRESH FAST ON DEMAND,缺一即退化为全量刷新或报ORA-12015。
能用,但必须分三步走清:先通db link、再建日志、最后建mv并指定fast刷新。漏任一环,refresh fast 就会退化成全量刷,甚至报错 ora-12015: cannot create a fast refresh materialized view from a complex query。
DB Link 必须可连且权限到位
物化视图远程查询依赖 DB Link 的底层连通性,不是“创建完就等于能用”。常见卡点是权限或网络配置未生效:
- 必须用
CREATE PUBLIC DATABASE LINK(非 public 的 link 只对创建者可见,而 MV 刷新由 Oracle 内部调度用户触发,非当前会话) - 目标库用户需有
CREATE DATABASE LINK权限;源库对应用户需有SELECT权限,且该用户在源库中不能被ALTER USER ... ACCOUNT LOCK - tnsnames.ora 中的 alias 名必须与 DB Link 定义中
USING后的字符串完全一致(大小写敏感),且监听必须已 reload(lsnrctl reload) - 测试连通性别只查
SELECT * FROM dual@mylink,要查真实表:SELECT COUNT(*) FROM scott.emp@mylink,否则可能掩盖权限不足问题
源库必须建物化视图日志,且字段要对得上
没有日志,REFRESH FAST 就不可能成立——Oracle 没地方读增量变更。关键细节常被忽略:
- 日志必须在 源库 创建,不是目标库;语句如:
CREATE MATERIALIZED VIEW LOG ON scott.emp WITH PRIMARY KEY, ROWID INCLUDING NEW VALUES -
WITH PRIMARY KEY要求源表有主键;若无主键,改用WITH ROWID,但后续 MV 定义中必须显式写REFRESH FAST WITH ROWID,且 SELECT 列里必须包含ROWID - 如果 MV 查询用了
WHERE deptno = 10,日志本身不需要包含deptno字段,但源表该字段必须可被索引(否则 FAST 刷新时过滤效率极低) - 建完日志后,务必确认日志表已生成:
SELECT * FROM mlog$_emp(表名前缀为mlog$+ 基表名小写)
物化视图定义必须匹配刷新模式要求
哪怕 DB Link 和日志都对了,MV 语句写错一个字,FAST 就会静默降级为 COMPLETE,你却看不出异常——直到某次刷新耗时从 2 秒变成 2 分钟。
- 首次建 MV 时,
BUILD IMMEDIATE是安全选择;但若数据量大,建议先BUILD DEFERRED,再手动DBMS_MVIEW.REFRESH('mv_name', 'C'),避免建表过程锁太久 - 必须显式声明
REFRESH FAST ON DEMAND(不能只写REFRESH FAST,后者语法不合法) - 如果源表用
PRIMARY KEY日志,MV 定义中可省略WITH PRIMARY KEY;但如果用ROWID日志,MV 必须带REFRESH FAST WITH ROWID - WHERE 过滤条件可以写,但不能含子查询、函数表达式(如
TO_CHAR(hiredate))、DISTINCT或聚合——这些会让 Oracle 认定为“complex query”,直接拒绝 FAST
调度和刷新失败时,别只看 JOB 状态
DBMS_SCHEDULER 任务跑成功 ≠ MV 刷新成功。Oracle 把刷新失败藏得很深:
- 查刷新历史别只看
DBA_SCHEDULER_JOB_LOG,重点看DBA_MVIEW_REFRESH_TIMES和DBA_MVIEWS中的LAST_REFRESH_DATE与STALENESS(值为STALE表示数据已过期) - 手动刷新调试时,加
ATOMIC_REFRESH => FALSE参数(如DBMS_MVIEW.REFRESH('mv_emp_dept10', 'F', ATOMIC_REFRESH => FALSE)),能避免因中间表空间不足导致整个事务回滚 - 如果刷新报
ORA-12008: error in materialized view refresh path,接着查DBA_MVIEW_ANALYSIS的ERROR_MSG列,或直接查DBA_MVIEW_LOGS看日志是否被截断(LOG_TABLE字段为空说明日志失效) - 不要长期依赖 MV 自带的
NEXT调度语法(如NEXT SYSDATE + 30/(24*60)),它无法重试、无告警;生产环境建议用DBMS_SCHEDULER.CREATE_JOB显式控制,并绑定MAX_FAILURES和EMAIL_NOTIFICATION
最易被跳过的环节是:建完 MV 后没验证 STALENESS 是否为 FRESH,也没手动跑一次 REFRESH 看日志表是否真有新记录落进 mlog$。只要这一步没闭环,后面所有定时任务都是空中楼阁。


















