postgresql物化视图必须显式刷新才能更新数据,不刷新则始终返回旧快照;默认创建即填充(with data),若误用with no data则查时报错“has not been populated”,需先执行refresh初始化。

物化视图不是“创建完就自动更新”的对象,它必须显式刷新才能反映底层表变化;不刷新,查到的永远是创建那一刻或上次刷新时的数据。
PostgreSQL 中创建物化视图要避开 WITH NO DATA 陷阱
默认 CREATE MATERIALIZED VIEW 会执行查询并填充数据(即隐含 WITH DATA)。但如果你加了 WITH NO DATA,创建后立刻查会报错:
ERROR: 55000: materialized view "my_mv" has not been populated HINT: Use the REFRESH MATERIALIZED VIEW command.
常见误操作是复制模板时没删掉 WITH NO DATA,结果建完不能用。确认是否已填充,可查 pg_class 表:
SELECT relispopulated FROM pg_class WHERE relname = 'my_mv';
返回 f 就说明还没刷过数据。
- 新建即用:省略
WITH NO DATA,或显式写WITH DATA - 延迟填充:只在需要时才
REFRESH MATERIALIZED VIEW my_mv,适合初始化耗时长、基表尚未就绪的场景 -
IF NOT EXISTS可防止重复创建报错,但不会覆盖已有定义——结构不一致也不会自动修正
REFRESH MATERIALIZED VIEW 的两种模式影响锁和可用性
默认刷新是全量重算 + 排他锁,期间所有读写都会被阻塞。高并发系统下容易卡住业务。
想避免锁表,必须用 CONCURRENTLY 模式:
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
但它有硬性前提:
- 物化视图必须有唯一索引(通常是主键或
UNIQUE约束) - 不能在事务块里执行(即不能包在
BEGIN; ...; COMMIT;中) - 刷新期间旧数据仍可读,新查询逐步看到更新结果,不是原子切换
没唯一索引却加 CONCURRENTLY,会直接报错:
ERROR: cannot refresh materialized view "my_mv" concurrently
Oracle 和 SQL Server 的“刷新”概念完全不同
Oracle 真有 REFRESH 命令,但方式选错会导致数据陈旧:
-
REFRESH COMPLETE:全量重建,安全但慢,需对基表有SELECT权限 -
REFRESH FAST:只应用物化视图日志变更,快但要求基表提前建日志(CREATE MATERIALIZED VIEW LOG ON t),且查询不能含ROWNUM、DISTINCT等限制项 -
REFRESH FORCE:先试FAST,失败则退到COMPLETE;但若日志缺失,会静默降级,表面成功实则数据未更新
SQL Server 根本没有 REFRESH 命令——它的“索引视图”靠 DML 触发器隐式维护,前提是查询满足严格条件(如 SCHEMABINDING、唯一聚集索引),否则优化器根本不会使用它。
MySQL 没有原生物化视图,别信“CREATE MATERIALIZED VIEW”语法
MySQL 官方至今不支持物化视图。所谓“实现”,本质是手动建表 + 定时任务模拟:
CREATE TABLE order_summary_mv AS SELECT ...; -- 后续靠定时脚本或事件调度器定期执行: INSERT INTO order_summary_mv SELECT ... ON DUPLICATE KEY UPDATE ...;
这种方案的问题很实在:
- 没有一致性保障:刷新过程中查表可能看到部分新、部分旧数据
- 无法利用数据库原生的查询重写机制(如 PostgreSQL 的
enable_material优化器开关) - 维护成本高:每次基表结构变更,都要同步改物化表和刷新逻辑
真正需要物化视图能力时,要么换 PostgreSQL/Oracle,要么用外部工具(如 Materialize、dbt)做增量计算层——数据库内建能力,不能靠“看起来像”来凑。










