3. 物化视图的创建语法

创建物化视图,说白了就是给查询结果拍一张「快照」。但怎么拍、拍完要不要马上存、存到哪个表空间,这里头有不少讲究。我刚开始用 Oracle 时,就因为在 WITH DATA 和 WITH NO DATA 之间选错了,导致一个批处理任务跑崩了——嗯,后面我会细说这个坑。

3.1 基础语法:CREATE MATERIALIZED VIEW

先看最核心的骨架:

CREATE MATERIALIZED VIEW view_name
[ TABLESPACE tablespace_name ]
[ BUILD { IMMEDIATE | DEFERRED } ]
[ REFRESH { FAST | COMPLETE | FORCE } ]
[ ON { DEMAND | COMMIT } ]
[ WITH { PRIMARY KEY | ROWID } ]
AS
  SELECT ... FROM ... WHERE ...;

别被这么多选项吓到。实际项目中,80% 的场景只需要前三个参数。我个人习惯先写一个最小版本跑通,再慢慢加选项。

核心要点:CREATE MATERIALIZED VIEW 后面必须跟 AS SELECT 子句。没有查询,就没有物化视图。

3.2 WITH DATA 与 WITH NO DATA:要不要马上填充?

这是新手最容易踩坑的地方。两个选项的区别其实很直白:

选项 行为 适用场景
WITH DATA(默认) 创建后立即执行查询,把结果写入物化视图 数据量小、查询快、需要马上可用
WITH NO DATA 只创建结构,不填充数据,视图处于不可查询状态 数据量大、查询慢、想先建结构再择机刷新

举个例子:

-- 立即填充(默认行为)
CREATE MATERIALIZED VIEW mv_sales_summary
WITH DATA
AS
  SELECT region, SUM(amount) total_sales
  FROM sales
  GROUP BY region;

-- 只建结构,稍后再填
CREATE MATERIALIZED VIEW mv_sales_summary
WITH NO DATA
AS
  SELECT region, SUM(amount) total_sales
  FROM sales
  GROUP BY region;

-- 稍后手动填充
REFRESH MATERIALIZED VIEW mv_sales_summary;

我曾经踩过的坑:有一次在线上库创建物化视图,忘了加 WITH NO DATA,结果源表有 2 亿行,创建语句跑了 40 分钟还没结束,把临时表空间撑爆了。从那以后,大表场景我必用 WITH NO DATA,先建结构,再在维护窗口用 REFRESH 慢慢填。

为什么会这样?因为 WITH DATA 是同步执行的。如果你的查询涉及多表 JOIN 或聚合,数据量又大,它会把当前会话卡住。你想想看,生产环境里一个 DDL 跑半小时,DBA 不找你喝茶才怪。

3.3 TABLESPACE 指定:把数据放到该放的地方

物化视图本质上是一张表,它需要物理存储空间。TABLESPACE 就是告诉数据库:把这张「快照表」放在哪个表空间里。

我建议遵循两个原则:

  • 读写分离:物化视图放只读表空间,源表放读写表空间
  • 性能隔离:高频访问的物化视图放 SSD 表空间,低频的放 HDD 表空间
-- 指定表空间
CREATE MATERIALIZED VIEW mv_orders_monthly
TABLESPACE ts_mv_data
WITH DATA
AS
  SELECT DATE_TRUNC('month', order_date) AS month,
         COUNT(*) AS order_count,
         SUM(amount) AS total_amount
  FROM orders
  GROUP BY DATE_TRUNC('month', order_date);

小技巧:如果不指定 TABLESPACE,物化视图会创建在用户的默认表空间里。我个人习惯在创建语句里显式指定,避免后续迁移的麻烦。

3.4 完整语法结构图

下面这张图把创建语法的核心流程串起来了,方便你对照理解:

物化视图创建语法结构图 CREATE MATERIALIZED VIEW 语法结构 CREATE MATERIALIZED VIEW 视图名称 TABLESPACE 子句 WITH DATA/NO DATA schema.view_name 指定物理存储位置 立即填充 / 延迟填充 AS SELECT 查询语句 SELECT 字段 FROM 源表 WHERE 条件 GROUP BY HAVING 过滤 💡 创建后可用 REFRESH MATERIALIZED VIEW 手动刷新数据 WITH NO DATA 创建的视图必须先 REFRESH 才能查询

3.5 实战建议:三种场景怎么选

根据我这些年的经验,不同场景的创建策略可以总结成一张表:

场景 推荐语法 理由
小表(< 10万行) WITH DATA + 默认表空间 创建快,直接可用,省事
大表(> 100万行) WITH NO DATA + 指定表空间 避免长时间阻塞,存储可规划
生产环境首次部署 WITH NO DATA + 专用表空间 先验证结构,再择机填充,风险最低

我的习惯:不管数据量大小,我写创建语句时都会把 TABLESPACE 带上。哪怕暂时用默认表空间,也显式写出来——这样以后查 DDL 时一眼就能看到存储位置,不用去猜。