引用分区子表必须显式创建含rowid和including new values的日志,且需join父表、避免外连接、聚合视图须日志与查询字段严格对齐。

引用分区子表必须显式建日志,且含 ROWID + INCLUDING NEW VALUES
引用分区(Reference Partitioning)的子表本身不自动继承父表物化视图日志,哪怕它和父表共用相同分区键。Oracle 在 FAST REFRESH 时需要精确捕获每行变更前后的值,尤其在 ON COMMIT 场景下——漏掉 INCLUDING NEW VALUES 会导致刷新直接退化为 COMPLETE,报 ORA-12032 或静默失败。
常见错误现象:建完 MV 后执行 DBMS_MVIEW.REFRESH 报 ORA-12052: cannot use rowid from restricted row source,本质是日志没记录新值,无法做增量比对。
-
CREATE MATERIALIZED VIEW LOG ON child_table WITH ROWID, SEQUENCE(child_id, parent_id) INCLUDING NEW VALUES;——child_id是子表主键,parent_id是外键列,必须出现在SEQUENCE()中 - 即使父表已建日志,子表日志也必须独立创建,且字段覆盖所有 JOIN 和 WHERE 引用列(例如
WHERE c.parent_id = p.id,则parent_id和id都要进各自日志) - 分区裁剪不影响日志范围:日志必须建在整个子表上,不能只建在某个分区(如
P202409),否则跨分区更新会丢失变更记录
父表必须纳入物化视图定义,否则外键约束检查失败
引用分区子表的外键指向父表,但 Oracle 的 FAST REFRESH(尤其是 ON COMMIT)会在刷新时校验外键值是否存在于当前物化视图可见的数据中。如果 MV 定义里只查子表、没包含父表,就会反复报 ORA-12008 + ORA-02291,不是数据错,而是 MV 结构缺父表上下文。
典型疏漏:用 WHERE sale_date >= ADD_MONTHS(SYSDATE, -3) 过滤子表分区,却没把父表 product 加进 SELECT,导致部分 prod_id 值在 MV 中“不可见”,触发约束失败。
- 必须显式 JOIN 父表,哪怕只取其主键或关键列:
SELECT c.*, p.prod_name FROM child_table c JOIN parent_table p ON c.parent_id = p.id - 若父表过大,不宜全量加载,改用
REFRESH FAST ON DEMAND并手动控制顺序:先刷父表 MV,再刷子表 MV - 禁止用子查询替代 JOIN(如
(SELECT prod_name FROM parent_table WHERE id = c.parent_id)),子查询直接禁用 FAST
JOIN 只能用 INNER,LEFT JOIN 必须拆成 UNION ALL
引用分区子表参与的关联查询,一旦写 LEFT JOIN,建 MV 就会报 ORA-12054,刷新时报 ORA-32313。Oracle 不解析外连接语义,无法确定 NULL 行的增量生命周期。
安全做法是人工等价拆解,但必须满足两个前提:两部分字段结构完全一致;NOT EXISTS 子句里只能引用右表主键或唯一键列。
- 第一部分走 INNER JOIN:
SELECT c.id, c.val, p.name FROM child_table c JOIN parent_table p ON c.parent_id = p.id - 第二部分补左表孤儿行:
SELECT c.id, c.val, CAST(NULL AS VARCHAR2(50)) FROM child_table c WHERE NOT EXISTS (SELECT 1 FROM parent_table p WHERE p.id = c.parent_id) - 两部分
UNION ALL后,所有字段类型、长度、NULL 性必须严格对齐,否则后续刷新可能因结构不稳而中断
聚合类物化视图需 COUNT(*) + 全 GROUP BY 列 + 所有基表日志对齐
如果物化视图带 COUNT(*) 或其他聚合函数(如 SUM、AVG),引用分区子表的日志不仅得含自身变更列,还要覆盖所有被 JOIN 和 WHERE 引用的父表列——否则 DBMS_MVIEW.EXPLAIN_MVIEW 返回 fastrefreshable = FALSE,且无明确报错提示。
容易被忽略的一点:父表日志里的 SEQUENCE() 必须包含子表 WHERE 条件中用到的列,比如子表过滤 c.status = 'A',那父表日志虽不直接存 status,但若该列参与了 JOIN 路径(如通过中间表),就得一并纳入。
- 子表日志:含
ROWID、SEQUENCE(child_id, parent_id, status)、INCLUDING NEW VALUES - 父表日志:含
ROWID、SEQUENCE(id, category)(假设category出现在 JOIN 条件中) - MV 查询中
SELECT cl.class_name, COUNT(*) FROM child_table c ... GROUP BY cl.class_name→cl.class_name必须出现在 SELECT 列表中,不能省略
最棘手的不是语法,而是日志字段和查询字段的隐式耦合——少一个列,FAST 就断;多一个无关列,不影响但浪费空间;错位一个类型,刷新中途就崩。动手前务必跑一遍 DBMS_MVIEW.EXPLAIN_MVIEW 看输出,别依赖建表不报错就以为 OK。











