分区物化视图是PolarDB PostgreSQL版提供的高级物化视图特性,能够基于分区表自动创建对应的分区物化视图,实现分区级别的增量刷新,大幅提升刷新效率。
概述
分区物化视图支持两种分区对齐模式:
等比对齐(Proportional):源表分区与物化视图分区一一对应。每个源分区对应一个子物化视图,刷新时仅刷新数据发生变化的分区。适用于源表分区粒度与物化视图查询粒度一致的场景。
上卷对齐(Rollup):物化视图按时间维度进行上卷聚合,将多个源分区的数据合并到一个物化视图分区中。例如源表按日分区,物化视图按月聚合。系统会根据查询定义中的
IMMUTABLE聚合表达式(如LEFT(date_str, 6))自动识别上卷关系。
两种模式使用相同的接口,系统根据查询定义自动判断对齐类型。
适用范围
扩展
需先执行以下SQL安装polar_ivm扩展:
CREATE EXTENSION IF NOT EXISTS polar_ivm;分区类型要求
分区类型 | 支持状态 |
RANGE 分区 | 支持 |
LIST 分区 | 不支持 |
HASH 分区 | 不支持 |
多列分区键 | 不支持(仅支持单列) |
查询定义要求
分区键必须在 GROUP BY 中:聚合查询时,分区键(或其上卷表达式)必须出现在
GROUP BY子句中。分区表不能在子查询中:分区表不能出现在
WHERE子查询、FROM子查询或CTE中。分区表不能在 JOIN 可空侧:
LEFT JOIN时分区表必须在左侧,RIGHT JOIN时分区表必须在右侧。不能多次引用分区表:查询中不能多次引用同一个分区表。
不能引用其他物化视图:查询中不能引用其他已有的物化视图。
不能包含易变函数:不能使用
CURRENT_DATE、random()等非不可变函数。
增量刷新机制
分区物化视图通过版本追踪实现智能增量刷新:
场景 | 刷新行为 |
分区数据未变化 | 跳过该分区(除非 |
分区数据发生变化 | 仅刷新该分区。 |
JOIN 的其他表数据变化 | 刷新所有分区。 |
源表新增/删除分区 | 刷新时自动同步结构并刷新新分区。 |
接口说明
分区物化视图通过polar_matview schema 提供三个存储过程管理其生命周期:create_partition_mv创建、refresh_partition_mv刷新、drop_partition_mv删除。三个存储过程均需以CALL方式调用。
创建分区物化视图
使用polar_matview.create_partition_mv存储过程创建分区物化视图:
CALL polar_matview.create_partition_mv(
mv_schema => 'public',
mv_name => 'my_mv',
ref_partition_table => 'public.orders'::regclass::oid,
view_def => 'SELECT order_date, COUNT(*) AS cnt FROM public.orders GROUP BY order_date',
use_imci => FALSE
);参数说明:
参数 | 类型 | 默认值 | 说明 |
| NAME | 必填 | 物化视图所在的schema。 |
| NAME | 必填 | 物化视图名称。 |
| OID | 必填 | 源分区表的OID,可通过 |
| TEXT | 必填 | 物化视图的 |
| BOOLEAN | FALSE | 是否使用列存索引(IMCI)加速创建和刷新。 |
对齐模式判断:系统根据view_def中的查询定义自动判断对齐模式。如果GROUP BY中的分区键表达式与源表分区键完全一致,则为等比对齐。如果GROUP BY中使用了IMMUTABLE聚合表达式(如LEFT(col, 6)),则自动识别为上卷对齐。
“上卷对齐”会使用GROUP BY中第一个带有分区键的表达式作为物化视图的分区键,请调整视图定义,将需要的分区表达式放在前面。
刷新分区物化视图
使用polar_matview.refresh_partition_mv存储过程刷新分区物化视图:
CALL polar_matview.refresh_partition_mv(
mv_schema => 'public',
mv_name => 'my_mv',
use_imci => FALSE,
force_refresh => FALSE
);参数说明:
参数 | 类型 | 默认值 | 说明 |
| NAME | 必填 | 物化视图所在的schema。 |
| NAME | 必填 | 物化视图名称。 |
| BOOLEAN | FALSE | 是否使用列存索引(IMCI)加速刷新。 |
| BOOLEAN | FALSE | 设为 |
刷新行为:
默认情况下,刷新时会自动同步源表的分区结构变化(新增/删除分区),然后仅刷新数据版本发生变化的分区。
设置
force_refresh=TRUE时,忽略版本检查,强制刷新所有分区。
删除分区物化视图
使用polar_matview.drop_partition_mv存储过程删除分区物化视图及其所有子分区物化视图,同时清理相关元数据:
CALL polar_matview.drop_partition_mv(
mv_schema => 'public',
mv_name => 'my_mv'
);参数说明:
参数 | 类型 | 默认值 | 说明 |
| NAME | 必填 | 物化视图所在的schema。 |
| NAME | 必填 | 物化视图名称。 |
使用示例
等比对齐示例
源表按日分区,物化视图也按日分区,源分区与物化视图分区一一对应。
-- 创建源分区表 (RANGE 按 order_date 单列分区)
CREATE TABLE orders (
order_id BIGSERIAL,
order_date DATE NOT NULL,
amount NUMERIC(12, 2)
) PARTITION BY RANGE (order_date);
CREATE TABLE orders_20260101 PARTITION OF orders
FOR VALUES FROM ('2026-01-01') TO ('2026-01-02');
CREATE TABLE orders_20260102 PARTITION OF orders
FOR VALUES FROM ('2026-01-02') TO ('2026-01-03');
-- 创建等比对齐的分区物化视图:GROUP BY 使用分区键 order_date 本身
CALL polar_matview.create_partition_mv(
mv_schema => 'public',
mv_name => 'orders_daily_mv',
ref_partition_table => 'public.orders'::regclass::oid,
view_def => 'SELECT order_date, COUNT(*) AS cnt, SUM(amount) AS total FROM public.orders GROUP BY order_date',
use_imci => FALSE
);
-- 刷新分区物化视图(仅刷新数据发生变化的分区)
CALL polar_matview.refresh_partition_mv(
mv_schema => 'public',
mv_name => 'orders_daily_mv'
);上卷对齐示例
源表按细粒度分区,物化视图按粗粒度聚合,多个源分区合并到一个物化视图分区。
示例:TEXT 类型分区键 + LEFT() 上卷(按日分区源表 → 按月聚合物化视图)
-- ========== 1. 创建按日分区的源表(分区键为 TEXT 类型 'YYYYMMDD') ==========
DROP SCHEMA IF EXISTS report CASCADE;
CREATE SCHEMA report;
CREATE TABLE report.daily_data (
log_date TEXT NOT NULL, -- 格式:'YYYYMMDD',如 '20240105'
value INTEGER
) PARTITION BY RANGE (log_date);
-- 每个源分区对应一天
CREATE TABLE report.daily_data_20240105 PARTITION OF report.daily_data
FOR VALUES FROM ('20240105') TO ('20240106');
CREATE TABLE report.daily_data_20240115 PARTITION OF report.daily_data
FOR VALUES FROM ('20240115') TO ('20240116');
CREATE TABLE report.daily_data_20240125 PARTITION OF report.daily_data
FOR VALUES FROM ('20240125') TO ('20240126');
CREATE TABLE report.daily_data_20240205 PARTITION OF report.daily_data
FOR VALUES FROM ('20240205') TO ('20240206');
CREATE TABLE report.daily_data_20240215 PARTITION OF report.daily_data
FOR VALUES FROM ('20240215') TO ('20240216');
CREATE TABLE report.daily_data_20240305 PARTITION OF report.daily_data
FOR VALUES FROM ('20240305') TO ('20240306');
INSERT INTO report.daily_data VALUES
('20240105', 20), ('20240115', 20), ('20240125', 20), -- 2024-01 合计 60
('20240205', 40), ('20240215', 50), -- 2024-02 合计 90
('20240305', 60); -- 2024-03 合计 60
-- ========== 2. 创建按月聚合的上卷物化视图 ==========
-- GROUP BY 中使用 LEFT(log_date, 6) 对分区键上卷,系统自动识别为上卷对齐
-- 6 个按日的源分区会合并到 3 个按月的物化视图分区
CALL polar_matview.create_partition_mv(
mv_schema => 'report',
mv_name => 'mv_monthly_summary',
ref_partition_table => 'report.daily_data'::regclass::oid,
view_def => 'SELECT LEFT(log_date, 6) AS month, SUM(value) AS total FROM report.daily_data GROUP BY LEFT(log_date, 6)',
create_no_data => TRUE
);
CALL polar_matview.refresh_partition_mv(
mv_schema => 'report',
mv_name => 'mv_monthly_summary'
);
-- ========== 3. 查询结果 ==========
SELECT * FROM report.mv_monthly_summary ORDER BY month;
-- month | total
-- --------+-------
-- 202401 | 60 -- 合并 20240105/20240115/20240125 三个日分区
-- 202402 | 90 -- 合并 20240205/20240215 两个日分区
-- 202403 | 60 -- 对应 20240305 一个日分区
-- ========== 4. 增量刷新(仅刷新数据发生变化的分区) ==========
INSERT INTO report.daily_data VALUES ('20240215', 10);
CALL polar_matview.refresh_partition_mv(
mv_schema => 'report',
mv_name => 'mv_monthly_summary'
);
-- 版本追踪自动识别:仅 20240215 所在的 2024-02 上卷分区被刷新,
-- 2024-01 和 2024-03 分区未变化被跳过
-- ========== 5. 新增日分区后再次刷新 ==========
CREATE TABLE report.daily_data_20240315 PARTITION OF report.daily_data
FOR VALUES FROM ('20240315') TO ('20240316');
INSERT INTO report.daily_data VALUES ('20240315', 30);
CALL polar_matview.refresh_partition_mv(
mv_schema => 'report',
mv_name => 'mv_monthly_summary'
);
-- 系统自动同步源表新增的分区结构,并把 20240315 数据上卷到 2024-03 分区
SELECT * FROM report.mv_monthly_summary ORDER BY month;
-- month | total
-- --------+-------
-- 202401 | 60
-- 202402 | 100 -- 因追加 20240215 的 10 变为 100
-- 202403 | 90 -- 因新增 20240315 的 30 变为 90上卷表达式必须是IMMUTABLE函数。PostgreSQL内置的date_trunc()与to_char()是STABLE函数(依赖会话参数),不能直接用作分区键。若源表分区键是DATE类型且需要按月上卷,建议将分区键改为TEXT类型(如'YYYYMMDD'格式)后使用LEFT()上卷,或自行封装一个IMMUTABLE的日期截断函数。