oracle 19c 不支持基于视图创建快速刷新物化视图,因日志只能建在基表上;必须展开视图逻辑、显式引用基表并为其分别建日志,且select中须为每个基表rowid指定别名。
物化视图定义中用了 view 而非基表,refresh fast 直接不可用
oracle 19c 不允许基于视图(view)创建支持 refresh fast 的物化视图。哪怕该视图只封装了一层 select * from t,只要物化视图的 select 列表里出现的是视图名,而非真实基表名,dbms_mview.explain_mview 就会返回 msgtxt = "no log on base table" 或 "refresh fast is not possible" —— 因为日志只能建在基表上,不能建在视图上。
常见错误写法:
CREATE MATERIALIZED VIEW mv_from_v AS SELECT * FROM my_view;
即使 my_view 是简单包装,Oracle 也完全不识别其底层依赖关系。刷新引擎在运行时找不到 MLOG$_my_view(根本不存在),也无法反向推导出该对哪张基表读日志,于是静默退化为 COMPLETE。
为什么不能让 Oracle 自动“穿透”视图找基表
物化视图快速刷新不是 SQL 重写,而是基于变更日志的确定性增量应用。它依赖三个硬性锚点:行定位(ROWID 或主键)、变更顺序(SEQUENCE)、新旧值捕获(INCLUDING NEW VALUES)。这些必须绑定到物理存储的基表,而视图没有 ROWID、不存数据、不产生 DML 日志。
-
ROWID在视图中无意义——视图行没有物理地址 - 视图上的
INSERT/UPDATE/DELETE若被允许(如 INSTEAD OF 触发器),也不会触发基表的物化视图日志记录 - 即使视图只查一张表,Oracle 也不做“逻辑等价推导”,这是设计使然,不是限制可绕过
替代方案:绕开视图,直接引用基表并显式补全所需字段
若业务强依赖视图逻辑,不要在物化视图定义里直接 FROM my_view,而应把视图定义“展开”进物化视图 SQL,并确保所有字段来源清晰、可追溯到具体基表列。
例如,假设:
CREATE VIEW v_sales AS SELECT s.id, s.amount, c.name cust_name FROM sales s JOIN customers c ON s.cust_id = c.id;
则正确做法是:
CREATE MATERIALIZED VIEW mv_sales_summary AS SELECT s.id, s.amount, c.name cust_name FROM sales s, customers c WHERE s.cust_id = c.id;
然后必须为两张基表分别建日志:
CREATE MATERIALIZED VIEW LOG ON sales WITH PRIMARY KEY, SEQUENCE(id, amount, cust_id) INCLUDING NEW VALUES;CREATE MATERIALIZED VIEW LOG ON customers WITH PRIMARY KEY, SEQUENCE(id, name) INCLUDING NEW VALUES;
注意:物化视图 SELECT 中必须显式写出 s.ROWID s_rowid 和 c.ROWID c_rowid(若用 WITH ROWID),或确保主键列完整出现在查询中(若用 WITH PRIMARY KEY)。
容易被忽略的细节:WITH ROWID 在 JOIN 场景下必须显式别名
即便你已正确引用基表,如果没在物化视图定义中为每个基表的 ROWID 提供别名,REFRESH FAST 仍会失败,EXPLAIN_MVIEW 报 MSGNO = 2012("missing ROWID in SELECT list")。
正确写法必须包含:
SELECT s.id, s.amount, c.name,
s.ROWID s_rowid,
c.ROWID c_rowid
FROM sales s, customers c
WHERE s.cust_id = c.id;
这个 s_rowid 和 c_rowid 不参与业务逻辑,但刷新引擎靠它们定位每条变更来自哪个基表的哪一行。漏一个,整个快速刷新链就断了。











