创建千万级列表分区表的FAST刷新物化视图前,必须显式创建含PRIMARY KEY、ROWID、SEQUENCE(非聚合列)及INCLUDING NEW VALUES的物化视图日志,且需配合ON PREBUILT TABLE与手动分区继承。

物化视图创建前必须补全物化视图日志
直接对千万级列表分区表建 FAST 刷新物化视图,90% 以上会失败,报 ORA-12052 或 ORA-12015。这不是语法问题,而是基表没日志——Oracle 不会自动为分区表生成增量捕获元数据。
必须显式执行:CREATE MATERIALIZED VIEW LOG ON your_list_partitioned_table WITH PRIMARY KEY, ROWID, SEQUENCE(col1, col2) INCLUDING NEW VALUES。注意三点:
-
PRIMARY KEY是 FAST 刷新硬性前提,若原表无主键或唯一约束,先ALTER TABLE ... ADD CONSTRAINT ... PRIMARY KEY -
SEQUENCE后括号里必须包含物化视图SELECT中所有非聚合列,不能只写*或漏列 - 列表分区表不支持
INCLUDING NEW VALUES的自动扩展,但该子句仍需保留,否则日志不生效
ON PREBUILT TABLE 是千万级表唯一可行的起步方式
用 REFRESH COMPLETE 一次性加载千万行,可能锁表数小时、触发 UNDO 溢出甚至 OOM;而 REFRESH FAST 又依赖日志和结构匹配。折中方案是跳过初始构建,复用已有表结构。
实操步骤:
- 先建目标结构的空表:
CREATE TABLE mv_target AS SELECT * FROM your_list_partitioned_table WHERE 1=0 - 在该表上手动加索引、主键(如需),确保与后续 MV 逻辑一致
- 执行:
CREATE MATERIALIZED VIEW mv_target ON PREBUILT TABLE REFRESH FAST ON DEMAND AS SELECT ... FROM your_list_partitioned_table - 首次刷新前,用并行 INSERT 填充历史数据:
INSERT /*+ APPEND PARALLEL(4) */ INTO mv_target SELECT ...,再调DBMS_MVIEW.REFRESH('mv_target', 'F')
列表分区表的物化视图必须手动继承分区结构
Oracle 物化视图本身不继承基表分区特性,ON PREBUILT TABLE 场景下,其底层存储表仍是普通堆表——这意味着按分区键查询时无法剪枝,全表扫描照旧。
要真正获得性能收益,得手动让物化视图表也分区:
- 提前按相同列(如
status)创建列表分区表:CREATE TABLE mv_target (...) PARTITION BY LIST (status) (...) - 确保
ON PREBUILT TABLE指向的就是这张已分区的表 - 物化视图定义中的
SELECT必须显式列出列,不能用SELECT *,否则可能引入隐藏列或虚拟列,导致创建失败 - 分区键值必须与基表完全一致(包括大小写、空格),否则刷新时会因值不匹配报错
FAST 刷新失败时别急着删重试,先查日志内容
DBMS_MVIEW.EXPLAIN_MVIEW 返回的 capable_name = 'REFRESH_FAST_AFTER_INSERT' 为 FALSE,不代表不能 FAST,只是当前定义触碰了某条限制。
常见卡点:
- 基表有
INTERVAL分区?列表分区表不支持INTERVAL,但若误建了,日志会静默失效 -
SELECT里用了NVL、COALESCE或常量表达式?这些会让 Oracle 认为结果列可空性与原表不一致,触发ORA-12082 - 物化视图日志建在了子分区名上?必须建在主表名上,Oracle 不识别子分区别名
- 用户权限不足?除了
CREATE MATERIALIZED VIEW,还需对基表有SELECT和ON COMMIT REFRESH(如用该模式)权限
最易被忽略的是:列表分区表的分区键值在物化视图日志里不会自动注册,必须靠 SEQUENCE 显式声明,否则哪怕结构全对,FAST 刷新也会退化成 COMPLETE。











