on prebuilt table本质是将已存在表“认领”为物化视图存储载体,不重建表而仅登记字典;要求表结构与查询严格一致(列名、顺序、类型、not null),且刷新依赖物化视图日志和基表可更新性。
物化视图 on prebuilt table 本质是“挂名”,不是“重建”
它不创建新表,只是把已存在的表“认领”为物化视图的存储载体。oracle 会跳过 create materialized view 的建表逻辑,直接在数据字典里登记该表为物化视图基表。前提是:表结构必须与查询定义严格一致(列名、顺序、类型、not null 约束),且不能有主键/唯一约束冲突。
- 常见错误现象:
ORA-12014: table does not contain a primary key constraint—— 这不是真要你加主键,而是 Oracle 检查时发现表没主键,但你的物化视图定义里写了WITH PRIMARY KEY;删掉这个子句,或确保表真有可用主键 - 使用场景:已有大宽表(比如 ETL 后的汇总表),想复用它支持快速刷新(
ON COMMIT或FAST),又不想迁移数据或停服重建 - 参数差异:
ON PREBUILT TABLE必须搭配USING INDEX(如果依赖主键)和明确的REFRESH子句;不能省略AS SELECT ...,哪怕只是个空壳查询(如SELECT * FROM existing_table WHERE 1=0不行,必须能匹配现有列)
列名和类型对齐比想象中更苛刻
Oracle 不做隐式转换,也不接受别名覆盖。你写 SELECT col_a AS id, col_b AS name FROM t,而原表是 id NUMBER, name VARCHAR2(50),只要 col_a 是 VARCHAR2,就会报 ORA-12082: "t.id" must be same type as "mv.id"。
- 容易踩的坑:原表用了
NUMBER(10),而查询里写了CAST(col AS NUMBER)—— 这会被视为无精度声明的NUMBER,类型不等价;必须写成CAST(col AS NUMBER(10)) - 性能影响:类型不一致会导致 Oracle 拒绝创建,或后续刷新失败;没有类型校验通过,
FAST REFRESH直接不可用 - 验证方法:用
DESC existing_table和SELECT column_name, data_type, data_length, data_precision FROM user_tab_columns WHERE table_name = 'EXISTING_TABLE' ORDER BY column_id对照查询语句里的每个表达式
刷新机制依赖原表的可更新性与日志配置
ON PREBUILT TABLE 视图能走快速刷新,前提是原表本身支持物化视图日志(CREATE MATERIALIZED VIEW LOG ON existing_table),且日志包含所有被查询引用的列。否则只能用 COMPLETE 刷新,那就失去预构建的意义了。
- 常见错误现象:
ORA-12008: error in materialized view refresh path+ORA-00942: table or view does not exist—— 实际是物化视图日志缺失,或日志没包含某列(比如查询用了UPPER(name),但日志没加INCLUDING NEW VALUES和对应列) - 兼容性注意:Oracle 12c 及以后支持在非主键列上建日志,但
FAST REFRESH ON COMMIT仍要求基表有主键,且日志含WITH PRIMARY KEY - 实操建议:先确认原表是否已有日志(查
user_mview_logs),再补全缺失列;刷新前务必执行EXEC DBMS_MVIEW.EXPLAIN_MVIEW('mv_name')看是否报告fastrefreshable = 'Y'
删除物化视图不会删底层表,但重命名或改结构会断链
这是最常被忽略的点:删掉物化视图只是清除字典关联,DROP MATERIALIZED VIEW mv_name 后,原表还在,数据完好。但如果你对原表执行 RENAME、ADD COLUMN、MODIFY COLUMN,物化视图就失效了——下次刷新会报 ORA-12003: materialized view does not exist 或列不匹配错误。
- 为什么这样做:Oracle 把物化视图和基表绑定在数据字典里,靠对象号(
object_id)硬关联,不是靠名字;改名后对象号不变,但元数据不同步;加列后查询定义未更新,类型校验失败 - 安全操作边界:只允许对原表做
INSERT/UPDATE/DELETE、TRUNCATE(需配合ATOMIC_REFRESH => FALSE);DDL 操作一律先DROP MV,再重建(带ON PREBUILT TABLE) - 检查手段:刷新前跑
SELECT mview_name, build_mode, refresh_mode, fast_refreshable FROM user_mviews WHERE mview_name = 'MV_NAME',若fast_refreshable变成NO,基本就是基表结构动过了










