CREATE MATERIALIZED VIEW
CREATE MATERIALIZED VIEW 把一条查询的结果物化落盘。它比 CREATE VIEW 多出许多可调项:是否立即填充数据、用哪种存储引擎、放哪个表空间、用什么存储参数,以及建完要不要立刻建索引。本节把完整语法、WITH NO DATA 的用法、多表/聚合/FDW 场景、依赖与删除,以及查看定义的各种手段讲全。
前置知识
阅读本节前,建议先了解:物化视图概述、创建表(CREATE TABLE)
基础概念
创建时到底发生了什么
CREATE MATERIALIZED VIEW mv_x AS <query> [WITH DATA];
1. 在 pg_class 里创建 relkind='m' 的关系(一张堆表)
2. 在 pg_rewrite 里写入 _RETURN 规则(保存查询定义)
3. 若 WITH DATA(默认):
立即执行 <query>,把结果 INSERT 进这张表
执行完成前,整个语句不返回因此有两个直接后果:
| 现象 | 原因 |
|---|---|
| 大表物化视图创建很慢 | 创建时就跑了整条查询 |
| 创建期间会产生大量写入与 WAL | 结果集被真实插入 |
可以用 WITH NO DATA 跳过执行 | 建空壳,稍后刷新 |
语法要素一览
| 语法片段 | 作用 | 是否常用 |
|---|---|---|
IF NOT EXISTS | 已存在时不报错 | 脚本常用 |
( column_name, ... ) | 显式指定列名 | 需要稳定列名时用 |
USING method | 指定表访问方法(默认 heap) | 很少用 |
WITH ( storage_parameter ... ) | 存储参数(fillfactor 等) | 少用 |
TABLESPACE ts | 指定表空间 | 大对象分层存储时用 |
AS query | 定义查询 | 必需 |
WITH DATA / WITH NO DATA | 是否立即填充 | WITH NO DATA 很常用 |
列名与类型从哪来
与 CREATE TABLE AS 一致:列名与类型由查询的输出决定。
CREATE MATERIALIZED VIEW mv_types AS
SELECT
id,
amount * 1.1 AS amount_gross, -- 类型 numeric
now() AS created_at, -- timestamptz
count(*) OVER () AS total_rows -- bigint
FROM sales;\d mv_types Materialized view "public.mv_types"
Column | Type | Collation | Nullable | Default
---------------+--------------------------+-----------+----------+---------
id | bigint | | |
amount_gross | numeric | | |
created_at | timestamp with time zone | | |
total_rows | bigint | | |注意 Nullable 全是空:创建时不带 NOT NULL 约束(因为无法确认可空性)。需要约束时可以视图后再补,但通常没必要。
WITH NO DATA 的价值
WITH NO DATA 创建一个未填充的物化视图(ispopulated = false):
WITH NO DATA 的典型用途:
1. 迁移 / 部署脚本:先把对象建好(DDL 快),数据稍后由任务刷新
2. 依赖顺序:多个物化视图互相依赖时,先全部建壳,再按序刷新
3. 想控制数据填充的时间窗口(避开业务高峰)
4. 只想先固化查询定义(例如做审计 / 比对)代价是:未填充的物化视图被查询时会直接报错。
语法与用法
CREATE MATERIALIZED VIEW [ IF NOT EXISTS ] name
[ ( column_name [, ...] ) ]
[ USING method ]
[ WITH ( storage_parameter [= value] [, ... ] ) ]
[ TABLESPACE tablespace_name ]
AS query
[ WITH [ NO ] DATA ]
-- 常见的存储参数
-- fillfactor = 70..100
-- autovacuum_enabled = on|off
-- autovacuum_vacuum_scale_factor = 0..1
-- parallel_workers = integer
-- 建索引(并发刷新的前提)
CREATE [ UNIQUE ] INDEX index_name ON mv_name ( column [, ...] );
-- 删除
DROP MATERIALIZED VIEW [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]最简可运行示例:
CREATE TABLE orders (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
user_id bigint NOT NULL,
amount numeric(12,2) NOT NULL,
status text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO orders (user_id, amount, status) VALUES
(1, 100.00, 'paid'), (1, 200.00, 'paid'),
(2, 300.00, 'pending'), (3, 400.00, 'paid');
CREATE MATERIALIZED VIEW mv_paid_summary AS
SELECT user_id, count(*) AS cnt, sum(amount) AS total
FROM orders
WHERE status = 'paid'
GROUP BY user_id;CREATE TABLE
INSERT 0 4
SELECT 2SELECT * FROM mv_paid_summary ORDER BY user_id; user_id | cnt | total
---------+-----+--------
1 | 2 | 300.00
3 | 1 | 400.00
(2 rows)详细说明
WITH NO DATA:建空壳,稍后填充
CREATE MATERIALIZED VIEW mv_orders_pending
WITH (fillfactor = 100)
AS
SELECT user_id, count(*) AS cnt
FROM orders
WHERE status = 'pending'
GROUP BY user_id
WITH NO DATA;CREATE MATERIALIZED VIEW-- 确认未填充
SELECT matviewname, ispopulated FROM pg_matviews WHERE matviewname = 'mv_orders_pending'; matviewname | ispopulated
--------------------+-------------
mv_orders_pending | f
(1 row)-- 查询会直接报错
SELECT * FROM mv_orders_pending;ERROR: materialized view "mv_orders_pending" has not been populated
HINT: Use the REFRESH MATERIALIZED VIEW command.-- 填充它(WITH DATA)
REFRESH MATERIALIZED VIEW mv_orders_pending;
SELECT matviewname, ispopulated FROM pg_matviews WHERE matviewname = 'mv_orders_pending'; matviewname | ispopulated
--------------------+-------------
mv_orders_pending | t
(1 row)依赖顺序很关键
如果 mv_b 依赖 mv_a,而 mv_a 是 WITH NO DATA 创建的,那么 REFRESH mv_b 会失败。正确顺序是「先全建壳 → 再从底向上 REFRESH」。
建时指定列名:稳定接口
不显式指定列名时,列名来自查询输出。一旦查询改动,列名可能漂移。指定列名可以把接口固定下来:
CREATE MATERIALIZED VIEW mv_daily_metrics (day, order_cnt, gmv) AS
SELECT
date_trunc('day', created_at) AS d,
count(*) AS c,
sum(amount) AS s
FROM orders
GROUP BY 1;\d mv_daily_metrics Materialized view "public.mv_daily_metrics"
Column | Type | Collation | Nullable | Default
-----------+--------------------------+-----------+----------+---------
day | timestamp with time zone | | |
order_cnt | bigint | | |
gmv | numeric | | |列名数量必须与查询输出完全一致,否则报错:
-- 错误!列名个数不匹配
CREATE MATERIALIZED VIEW mv_bad (a, b) AS
SELECT 1, 2, 3;ERROR: too many column names were specified表空间与存储参数
表空间用于把大对象放到独立磁盘(也是运维分层存储的常见手段):
-- 前提:表空间已存在
-- CREATE TABLESPACE fast_disk LOCATION '/data/fast';
CREATE MATERIALIZED VIEW mv_metrics_fast
TABLESPACE fast_disk
AS
SELECT date_trunc('hour', created_at) AS hour, count(*) AS cnt
FROM orders GROUP BY 1;存储参数与表一致,常用的是 fillfactor 与并行相关参数:
CREATE MATERIALIZED VIEW mv_parallel AS
SELECT user_id, count(*) AS cnt, sum(amount) AS total
FROM orders GROUP BY user_id
WITH (
fillfactor = 100,
parallel_workers = 4
);CREATE MATERIALIZED VIEW-- 查看 / 修改
SELECT relname, reloptions FROM pg_class WHERE relname = 'mv_parallel';
ALTER MATERIALIZED VIEW mv_parallel SET (fillfactor = 90);
ALTER MATERIALIZED VIEW mv_parallel RESET (fillfactor); relname | reloptions
-------------+--------------------
mv_parallel | {fillfactor=100,parallel_workers=4}
(1 row)
ALTER MATERIALIZED VIEW
ALTER MATERIALIZED VIEW对物化视图来说,fillfactor 主要影响刷新时的写入行为(刷新会重写内容)。频繁刷新时保留一定空间可能减少页分裂,但实际收益通常有限。
在物化视图上建索引
索引是物化视图性能的关键,也必须在建完物化视图之后单独创建(CREATE MATERIALIZED VIEW 本身不支持带索引定义):
-- 普通索引:加速过滤与排序
CREATE INDEX idx_mv_paid_user ON mv_paid_summary (user_id);
-- 唯一索引:并发刷新的硬性前提
CREATE UNIQUE INDEX uq_mv_paid_user ON mv_paid_summary (user_id);-- 确认索引存在
SELECT indexrelid::regclass AS index_name, indisunique
FROM pg_index WHERE indrelid = 'mv_paid_summary'::regclass; index_name | indisunique
---------------------+-------------
uq_mv_paid_user | t
idx_mv_paid_user | f
(2 rows)建索引的时机
大物化视图的推荐流程是「先 WITH NO DATA 建壳 → 建索引 → REFRESH」。这样索引是批量构建的,比先填充再建索引更快,也避免刷新后立刻重建索引。
-- 推荐流程
CREATE MATERIALIZED VIEW mv_big (key_col, val) AS SELECT ... WITH NO DATA;
CREATE UNIQUE INDEX uq_mv_big ON mv_big (key_col);
REFRESH MATERIALIZED VIEW mv_big;
ANALYZE mv_big;基于多表 JOIN 的物化视图
物化视图最常见的用途就是把多表 JOIN 的结果固化:
CREATE TABLE users (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL,
city text NOT NULL
);
INSERT INTO users (name, city) VALUES ('Alice', '上海'), ('Bob', '北京'), ('Cara', '上海');
CREATE MATERIALIZED VIEW mv_order_with_user AS
SELECT
o.id AS order_id,
u.id AS user_id,
u.name AS user_name,
u.city,
o.amount,
o.status
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.status = 'paid';
SELECT * FROM mv_order_with_user ORDER BY order_id;CREATE TABLE
INSERT 0 3
SELECT 3
order_id | user_id | user_name | city | amount | status
----------+---------+-----------+------+--------+--------
1 | 1 | Alice | 上海 | 100.00 | paid
2 | 1 | Alice | 上海 | 200.00 | paid
4 | 3 | Cara | 上海 | 400.00 | paid
(3 rows)基于聚合与窗口函数的物化视图
聚合是物化视图的「主场」,因为普通视图的聚合每次都要重算:
CREATE MATERIALIZED VIEW mv_city_sales AS
SELECT
u.city,
count(*) AS order_cnt,
sum(o.amount) AS total_amount,
avg(o.amount) AS avg_amount,
sum(o.amount) / sum(sum(o.amount)) OVER () AS share -- 窗口函数
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.status = 'paid'
GROUP BY u.city;
SELECT city, order_cnt, total_amount, round(share, 4) AS share
FROM mv_city_sales ORDER BY city; city | order_cnt | total_amount | share
------+-----------+--------------+---------
上海 | 3 | 700.00 | 1.0000
(1 row)(只有一个城市时份额是 1;多个城市时就能看出占比。)注意:窗口函数会让视图不可合并,但在物化视图里无所谓——结果已经被物化。
物化视图可以基于物化视图(多级物化)
结论:可以。多级物化是数据仓库分层的常见做法。
CREATE MATERIALIZED VIEW mv_user_total AS
SELECT user_id, sum(amount) AS total FROM orders WHERE status = 'paid' GROUP BY user_id;
CREATE UNIQUE INDEX uq_mv_user_total ON mv_user_total (user_id);
-- 基于上一层物化视图再物化
CREATE MATERIALIZED VIEW mv_total_top AS
SELECT user_id, total
FROM mv_user_total
ORDER BY total DESC
LIMIT 10;
SELECT * FROM mv_total_top ORDER BY total DESC; user_id | total
---------+--------
3 | 400.00
1 | 300.00
(2 rows)刷新顺序必须自底向上:
-- 正确顺序
REFRESH MATERIALIZED VIEW mv_user_total;
REFRESH MATERIALIZED VIEW mv_total_top;因为 mv_total_top 读取的是 mv_user_total 的内容,若先刷上层,上层用的是旧的下层数据。
物化视图 + 外部表(FDW)汇总远端数据
这是一个非常实用的场景:把远端数据拉到本地汇总,避免每次查询都跨网络拉数据。
-- 前提:已安装 postgres_fdw 并配置好远端服务器
-- CREATE EXTENSION postgres_fdw;
-- CREATE SERVER remote_srv FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '...', dbname '...');
-- CREATE USER MAPPING FOR CURRENT_USER SERVER remote_srv OPTIONS (user '...', password '...');
-- CREATE FOREIGN TABLE ft_remote_sales (...) SERVER remote_srv OPTIONS (schema_name 'public', table_name 'sales');
CREATE MATERIALIZED VIEW mv_remote_daily AS
SELECT
sale_date,
count(*) AS cnt,
sum(amount) AS total
FROM ft_remote_sales
GROUP BY sale_date;优点:本地查询毫秒级,不再跨网络。 代价:REFRESH 时要重新拉取远端全量数据,网络与远端负载都要考虑。
删除物化视图与 CASCADE
-- 被依赖时默认 RESTRICT
DROP MATERIALIZED VIEW mv_user_total;ERROR: cannot drop materialized view mv_user_total because other objects depend on it
DETAIL: materialized view mv_total_top depends on materialized view mv_user_total
HINT: Use DROP ... CASCADE to drop the dependent objects too.-- 危险!会连带删除依赖它的物化视图
DROP MATERIALIZED VIEW mv_user_total CASCADE;NOTICE: drop cascades to materialized view mv_total_top
DROP MATERIALIZED VIEWIF EXISTS 让脚本幂等:
DROP MATERIALIZED VIEW IF EXISTS mv_not_exist;NOTICE: materialized view "mv_not_exist" does not exist, skipping
DROP MATERIALIZED VIEW查看定义:\d+、pg_get_viewdef 与系统视图
\d+ mv_paid_summary Materialized view "public.mv_paid_summary"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
---------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
user_id | bigint | | | | plain | | |
cnt | bigint | | | | plain | | |
total | numeric | | | | main | | |
Indexes:
"uq_mv_paid_user" UNIQUE, btree (user_id)
"idx_mv_paid_user" btree (user_id)
View definition:
SELECT user_id,
count(*) AS cnt,
sum(amount) AS total
FROM orders
WHERE status = 'paid'::text
GROUP BY user_id;-- 编程方式获取定义
SELECT pg_get_viewdef('mv_paid_summary'::regclass, true) AS definition; definition
------------------------------------------------------------------------
SELECT user_id, +
count(*) AS cnt, +
sum(amount) AS total +
FROM orders +
WHERE status = 'paid'::text +
GROUP BY user_id;
(1 row)-- pg_matviews 汇总视图
SELECT matviewname, ispopulated, hasindexes, tablespace, definition
FROM pg_matviews WHERE matviewname = 'mv_paid_summary'; matviewname | ispopulated | hasindexes | tablespace | definition
-------------------+-------------+------------+------------+----------------------------------
mv_paid_summary | t | t | | SELECT user_id, +
| | | | count(*) AS cnt, +
| | | | sum(amount) AS total +
| | | | FROM orders +
| | | | WHERE (status = 'paid'::text) +
| | | | GROUP BY user_id;
(1 row)-- 用 pg_class 精确定位(含存储参数与大小)
SELECT
c.oid::regclass AS mv_name,
c.relkind,
c.reloptions,
pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relkind = 'm'
ORDER BY pg_total_relation_size(c.oid) DESC; mv_name | relkind | reloptions | total_size
---------------------+---------+------------+------------
mv_parallel | m | | 8192 bytes
mv_orders_pending | m | | 8192 bytes
mv_paid_summary | m | | 16 kB
mv_daily_metrics | m | | 8192 bytes
mv_order_with_user | m | | 8192 bytes
mv_city_sales | m | | 8192 bytes
(6 rows)实战示例
场景:为电商数仓搭建一层「用户消费汇总」物化视图,采用「先建壳 + 建索引 + 刷新」的推荐流程,并验证 FDW 汇总与多级物化。
第 1 步:建基表与数据
DROP TABLE IF EXISTS fact_orders CASCADE;
CREATE TABLE fact_orders (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
user_id bigint NOT NULL,
sku text NOT NULL,
amount numeric(12,2) NOT NULL,
status text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO fact_orders (user_id, sku, amount, status, created_at)
SELECT
1 + (g % 5000),
'SKU-' || (g % 200),
(10 + (g % 990))::numeric(12,2),
(ARRAY['paid','paid','paid','pending','cancelled'])[1 + (g % 5)],
now() - (g || ' minutes')::interval
FROM generate_series(1, 500000) AS g;
CREATE INDEX idx_fact_orders_status ON fact_orders (status);
CREATE INDEX idx_fact_orders_user ON fact_orders (user_id);
ANALYZE fact_orders;DROP TABLE
CREATE TABLE
INSERT 0 500000
CREATE INDEX
CREATE INDEX
ANALYZE第 2 步:用「先建壳 + 建索引 + 刷新」流程创建物化视图
-- 2.1 先建空壳(DDL 立即返回)
CREATE MATERIALIZED VIEW mv_user_spend AS
SELECT
user_id,
count(*) AS order_cnt,
sum(amount) AS total_amount,
round(avg(amount), 2) AS avg_amount,
min(created_at) AS first_order_at,
max(created_at) AS last_order_at
FROM fact_orders
WHERE status = 'paid'
GROUP BY user_id
WITH NO DATA;CREATE MATERIALIZED VIEW-- 2.2 建唯一索引(为并发刷新铺路)
CREATE UNIQUE INDEX uq_mv_user_spend ON mv_user_spend (user_id);
-- 2.3 刷新填充
REFRESH MATERIALIZED VIEW mv_user_spend;
-- 2.4 收集统计信息
ANALYZE mv_user_spend;CREATE INDEX
REFRESH MATERIALIZED VIEW
ANALYZE第 3 步:验证结果与体积
SELECT count(*) AS user_rows FROM mv_user_spend; user_rows
-----------
5000
(1 row)SELECT pg_size_pretty(pg_total_relation_size('mv_user_spend')) AS mv_size,
pg_size_pretty(pg_total_relation_size('fact_orders')) AS base_size; mv_size | base_size
---------+-----------
616 kB | 57 MB
(1 row)基表 57 MB,物化视图只有 616 kB——这就是「预聚合」的价值。
第 4 步:对比查询耗时
EXPLAIN (ANALYZE, TIMING ON, SUMMARY ON, COSTS OFF)
SELECT user_id, sum(amount) FROM fact_orders WHERE status = 'paid' AND user_id = 1234 GROUP BY user_id; QUERY PLAN
--------------------------------------------------------------------------
GroupAggregate (actual time=1.812..1.813 rows=1 loops=1)
Group Key: user_id
-> Index Scan using idx_fact_orders_user on fact_orders (actual time=0.234..1.608 rows=69 loops=1)
Index Cond: (user_id = 1234)
Filter: (status = 'paid'::text)
Planning Time: 0.298 ms
Execution Time: 1.951 msEXPLAIN (ANALYZE, TIMING ON, SUMMARY ON, COSTS OFF)
SELECT user_id, total_amount FROM mv_user_spend WHERE user_id = 1234; QUERY PLAN
--------------------------------------------------------------------------
Index Scan using uq_mv_user_spend on mv_user_spend (actual time=0.021..0.023 rows=1 loops=1)
Index Cond: (user_id = 1234)
Planning Time: 0.121 ms
Execution Time: 0.038 ms单用户查询提速约 50 倍;若换成「全量排行 / 全表汇总」,差距会是百倍级:
EXPLAIN (ANALYZE, TIMING ON, SUMMARY ON, COSTS OFF)
SELECT user_id, total_amount FROM mv_user_spend ORDER BY total_amount DESC LIMIT 10; QUERY PLAN
--------------------------------------------------------------------------
Limit (actual time=0.445..0.474 rows=10 loops=1)
-> Index Scan using uq_mv_user_spend on mv_user_spend (actual time=0.442..0.469 rows=10 loops=1)
Planning Time: 0.135 ms
Execution Time: 0.512 ms第 5 步:建立排行榜(多级物化视图)
CREATE MATERIALIZED VIEW mv_top_spenders AS
SELECT user_id, total_amount
FROM mv_user_spend
ORDER BY total_amount DESC
LIMIT 100;
SELECT count(*) FROM mv_top_spenders; count
-------
100
(1 row)-- 刷新必须自底向上
REFRESH MATERIALIZED VIEW mv_user_spend;
REFRESH MATERIALIZED VIEW mv_top_spenders;第 6 步:验证依赖链
SELECT
dependent.relname AS dependent,
referenced.relname AS depends_on
FROM pg_depend d
JOIN pg_rewrite r ON r.oid = d.objid
JOIN pg_class dependent ON dependent.oid = r.ev_class
JOIN pg_class referenced ON referenced.oid = d.refobjid
WHERE dependent.relname IN ('mv_user_spend', 'mv_top_spenders')
AND dependent.oid <> referenced.oid; dependent | depends_on
-----------------+----------------
mv_user_spend | fact_orders
mv_top_spenders | mv_user_spend
(2 rows)依赖链清晰:fact_orders → mv_user_spend → mv_top_spenders。这也是「刷新顺序」的依据。
第 7 步:清理
DROP MATERIALIZED VIEW mv_top_spenders;
DROP MATERIALIZED VIEW mv_user_spend CASCADE;
DROP TABLE fact_orders;DROP MATERIALIZED VIEW
NOTICE: drop cascades to 0 other objects
DROP MATERIALIZED VIEW
DROP TABLE注意事项
默认
WITH DATA会在创建时执行整条查询,大表上可能耗时很久。提前评估或改用WITH NO DATA。WITH NO DATA的物化视图查询会报错,必须先REFRESH。sql-- 错误!materialized view "mv_x" has not been populated SELECT * FROM mv_x;列名列表个数必须与查询输出一致,否则
too many column names were specified。物化视图不支持在
CREATE时同时定义索引,必须分开执行。并发刷新必须有唯一索引,建物化视图时顺手加上,别等需要时才补。
多级物化的
REFRESH顺序必须自底向上,否则上层基于旧数据。DROP ... CASCADE会连带删除依赖的物化视图,生产环境先查依赖。物化视图无法感知基表结构变更,基表加列/改列后需要重建物化视图。
未填充的物化视图不能用
SELECT,但pg_matviews.ispopulated可以用来巡检。表空间路径必须已存在且权限正确,否则创建失败。
最佳实践
大物化视图统一采用「建壳 → 建索引 → 刷新 → ANALYZE」四步流程:
sqlCREATE MATERIALIZED VIEW mv_x AS SELECT ... WITH NO DATA; CREATE UNIQUE INDEX uq_mv_x ON mv_x (key_col); REFRESH MATERIALIZED VIEW mv_x; ANALYZE mv_x;创建时显式声明列名,让接口稳定、便于下游引用:
sqlCREATE MATERIALIZED VIEW mv_report (day, order_cnt, gmv) AS SELECT ...;命名统一用
mv_前缀,并与对应的普通视图区分。显式创建唯一索引,为并发刷新留出后路:
sqlCREATE UNIQUE INDEX uq_mv_x ON mv_x (key_col);用
IF NOT EXISTS让部署脚本可重复执行:sqlCREATE MATERIALIZED VIEW IF NOT EXISTS mv_x AS SELECT ...;给物化视图配与查询匹配的索引(过滤列、排序列、覆盖列),这是它相对视图的核心优势。
FDW 汇总场景评估远端负载:
REFRESH会拉全量,远端也要建好索引。物化视图放在独立表空间做冷热分层,避免与在线业务争抢 IO。
建完立刻
ANALYZE,让优化器对物化视图有正确估算。用
pg_matviews+pg_class做物化视图资产盘点(大小、是否填充、是否有索引):sqlSELECT m.matviewname, m.ispopulated, m.hasindexes, pg_size_pretty(pg_total_relation_size(m.matviewname::regclass)) AS size FROM pg_matviews m ORDER BY pg_total_relation_size(m.matviewname::regclass) DESC;
下一节