PolarDB PostgreSQL版提供了增量物化视图(也称按需增量物化视图,DIMV)功能,通过物化视图日志(Materialized View Log,简称mlog)异步追踪基表的数据变更,在刷新时仅应用增量数据(delta),避免全量重算,从而提升物化视图的刷新效率。
背景介绍
物化视图本质上是把一条查询Q的结果预先计算并存储下来。普通物化视图在刷新时执行的是REFRESH MATERIALIZED VIEW,它会重新完整执行一遍Q:扫描所有基表、重新做连接和聚合,再用结果整体替换旧数据。当基表很大、但两次刷新之间只改动了少量行时,这种“全量重算”把绝大部分时间花在了重复计算没有变化的数据上。
增量物化视图换了一个思路:物化视图的结果是基表的函数,记作V = Q(基表)。如果能知道基表自上次刷新以来发生的变化量Δ(哪些行被插入、删除、更新),那么视图结果的变化量ΔV也只由Δ决定,而与那些没有变化的海量数据无关。于是刷新就从“重算整个V”变成“算出ΔV并把它合并进已有的V”—计算量正比于改动量,而不是基表总量。
实现这一思路需要解决三个问题,它们分别对应以下三个核心概念:
如何捕获基表的变更:在基表上创建触发器,将每次
INSERT/UPDATE/DELETE产生的变化行记录到一张日志表中,该表即物化视图日志(mlog),用于承载上文所述的Δ。如何将变更应用到视图:刷新时读取自上次刷新以来积累的mlog(即该时间段内的
Δ),据此计算ΔV并合并进物化视图。由于仅在您主动刷新时执行、且只处理增量,故称为按需增量刷新。如何回收已消费的日志:mlog随基表变更持续增长,当一条记录已被所有依赖它的物化视图刷新消费后即可删除,此过程称为日志清理(purge),用于防止日志无限膨胀。
功能概述
物化视图日志(mlog):在基表上创建mlog表,通过触发器记录
INSERT、UPDATE和DELETE产生的变更。按需增量刷新:刷新时读取自上次刷新以来的mlog数据并将其应用到物化视图。
日志清理(purge):清理已被所有依赖物化视图消费的mlog条目,避免日志持续增长。
适用范围
增量物化视图(DIMV)依赖polar_ivm插件。使用前请确认集群内核版本、扩展安装情况满足以下要求。
内核版本:PostgreSQL 14,且内核小版本需为2.0.14.20.46.0及以上版本。
安装 polar_ivm 扩展:在需要使用DIMV的数据库中执行以下SQL安装
polar_ivm扩展:CREATE EXTENSION IF NOT EXISTS polar_ivm;基表要求:所有参与DIMV的基表都必须有可唯一标识行的列,DIMV 借此在增量刷新时把 mlog 中记录的变更精确定位到视图行。
基表必须显式定义
PRIMARY KEY,支持单列主键和复合主键。没有主键的表不能作为DIMV基表。
使用说明
创建 DIMV 和 mlog
使用CREATE MATERIALIZED VIEW ... REFRESH FAST ON DEMAND ... WITH NO DATA创建DIMV时,系统会为涉及的基表自动创建mlog,并加入查询所需的列。推荐始终使用WITH NO DATA,由系统自动创建和补全mlog。
CREATE MATERIALIZED VIEW dimv_name
REFRESH FAST ON DEMAND
AS query
WITH NO DATA;每张基表只需要一个mlog,多个DIMV可以共享。如果已有mlog缺少新DIMV依赖的列,使用WITH NO DATA创建新DIMV时系统会自动补全。如果不使用WITH NO DATA,则需要提前创建mlog,否则创建DIMV会失败。
首次全量刷新建立基线
使用WITH NO DATA创建DIMV后,DIMV上没有数据,且尚未建立“增量刷新基线”(即尚未记录一个可信的起点事务ID)。此时如果直接调用polar_matview.incremental_refresh_mv()做增量刷新,会报错:
ERROR: No refresh snapshot found for incremental materialized view mv_name
ERROR: materialized view "mv_name" has not been populated
HINT: Use the REFRESH MATERIALIZED VIEW command.正确的启动流程是:先执行一次全量REFRESH MATERIALIZED VIEW建立增量基线,之后就可以正常调用polar_matview.incremental_refresh_mv()做按需增量刷新。
-- 1. 建基表并写入初始数据
CREATE TABLE t_dimv(id INT PRIMARY KEY, cat TEXT, amount NUMERIC);
INSERT INTO t_dimv VALUES(1,'a',10),(2,'a',20),(3,'b',30);
-- 2. 用 WITH NO DATA 创建 DIMV(系统自动为 t_dimv 创建 mlog)
CREATE MATERIALIZED VIEW mv_dimv
REFRESH FAST ON DEMAND
AS SELECT cat, SUM(amount) FROM t_dimv GROUP BY cat
WITH NO DATA;
-- 3. 首次全量刷新,建立增量基线
REFRESH MATERIALIZED VIEW mv_dimv;
SELECT * FROM mv_dimv ORDER BY cat;
-- 输出:
-- cat | sum
-- ----+-----
-- a | 30
-- b | 30
-- 4. 追加变更数据到基表
INSERT INTO t_dimv VALUES(4,'b',40),(5,'a',50);
-- 5. 按需增量刷新(仅应用增量数据,不重算全表)
SELECT polar_matview.incremental_refresh_mv('mv_dimv'::regclass::oid);
-- 返回 t 表示增量刷新成功
SELECT * FROM mv_dimv ORDER BY cat;
-- 输出:
-- cat | sum
-- ----+-----
-- a | 80
-- b | 70每个新建的DIMV都必须首次执行一次全量
REFRESH MATERIALIZED VIEW建立基线后,才能进入按需增量刷新阶段。首次全量刷新完成后,DIMV会注册到
polar_ivm.imatview_metadata元数据表,后续所有增量刷新(无论是手动polar_matview.incremental_refresh_mv()还是后台worker)都基于此基线执行。DIMV失效(如基表
TRUNCATE)后,也需要再次执行全量REFRESH MATERIALIZED VIEW重建基线,详见失效与恢复。
配置自动刷新与清理
DIMV 默认不会自动刷新。通过pg_cron扩展在数据库启动后拉起若干个长期运行的DIMV worker,再为需要自动刷新的DIMV配置刷新间隔,worker即可在后台自动完成增量刷新和mlog清理。
后台 worker 自动刷新
polar_matview.auto_refresh_worker_main()是一个不会主动退出的循环过程:worker 从调度队列领取到期任务,执行单个 DIMV 的增量刷新或单个 mlog 的清理。由于该过程需要长驻后台不间断运行,直接在前台会话执行会阻塞会话,实际使用中通过pg_cron扩展的@restart触发时机让集群启动时自动拉起 worker:
-- 1. 安装 pg_cron 扩展(如尚未安装)
CREATE EXTENSION IF NOT EXISTS pg_cron;
-- 2. 通过 pg_cron 在集群启动时后台拉起 DIMV 刷新 worker
-- 每个 cron.schedule 对应一个 worker,worker 数量按业务刷新压力配置
SELECT cron.schedule(
'dimv-auto-refresh-worker-1',
'@restart',
$$CALL polar_matview.auto_refresh_worker_main()$$
);
SELECT cron.schedule(
'dimv-auto-refresh-worker-2',
'@restart',
$$CALL polar_matview.auto_refresh_worker_main()$$
);每个调用对应一个worker,worker数量按业务刷新压力配置。这些worker同时负责自动刷新和mlog的自动清理。
worker会话的statement_timeout与polar_transaction_timeout必须为0,否则worker会被超时中断、反复重拉。这两项默认都为0时无需额外配置。任一不为0时,单独建一个专用角色并将其置0,再用该角色调度worker:
-- 建一个专用角色,并将两项超时参数置 0
CREATE ROLE dimv_worker LOGIN;
ALTER ROLE dimv_worker SET statement_timeout = 0;
ALTER ROLE dimv_worker SET polar_transaction_timeout = 0;
-- 用该角色调度 worker(通过 pg_cron 后台运行)
SELECT cron.schedule_in_database(
'dimv-auto-refresh-worker-1',
'@restart',
$$CALL polar_matview.auto_refresh_worker_main()$$,
'postgres', -- 目标数据库
'dimv_worker' -- 以该专用角色执行
);设置刷新间隔
为DIMV配置刷新间隔后,worker会按间隔自动增量刷新。系统同时会自动维护相关mlog的清理间隔,无需单独配置清理。通过polar_matview.set_refresh_interval()与polar_matview.reset_refresh_interval()为指定 DIMV 设置或取消刷新间隔(参数为物化视图 OID):
-- 设置某 DIMV 的自动刷新间隔为 1 秒
SELECT polar_matview.set_refresh_interval('dimv_name'::regclass::oid, '1 seconds'::interval);
-- 关闭某 DIMV 的自动刷新
SELECT polar_matview.reset_refresh_interval('dimv_name'::regclass::oid);调度器会扫描polar_ivm.imatview_metadata中所有已注册、且刷新间隔不小于0的DIMV,并按各自的间隔独立刷新。刷新是逐对象进行的,不会自动带动其依赖的其他DIMV。嵌套场景下需要为每一层分别设置间隔。
如无特殊原因,建议将刷新间隔设置为秒级而非分钟级。增量刷新的CPU开销只与增量数据量相关,而与刷新频率无关:拉长间隔并不会减少总开销,只会让变更在间隔内堆积,刷新时形成资源波峰。间隔越小,负载越平滑,波峰波谷被展平。
需要注意的是每次刷新自身有加锁等固定overhead,间隔过小会让这部分固定开销占比上升。因此将间隔设为0(尽快刷新)可能反而增大总体资源开销,仅在对刷新延迟有极致要求时才推荐使用。
某个基表被多个DIMV直接依赖时,该基表mlog的自动清理间隔取这些DIMV刷新间隔的最大值;所有直接依赖者都关闭自动刷新后,该基表mlog的自动清理也会关闭。
调度器只处理仍可增量更新的DIMV和有效mlog。DIMV或mlog失效时,需要先执行恢复操作。
完整示例
下面是一个从安装扩展到配置自动刷新的端到端示例,覆盖 DIMV 常规使用的全流程。
-- 1. 安装扩展
CREATE EXTENSION IF NOT EXISTS polar_ivm;
CREATE EXTENSION IF NOT EXISTS pg_cron;
-- 2. 启动后台 worker(数据库重启后自动拉起)
SELECT cron.schedule(
'dimv-auto-refresh-worker-1',
'@restart',
$$CALL polar_matview.auto_refresh_worker_main()$$
);
-- 3. 创建基表并写入初始数据
CREATE TABLE products (
product_id INT PRIMARY KEY,
name TEXT,
category TEXT,
price NUMERIC
);
CREATE TABLE order_items (
item_id BIGSERIAL PRIMARY KEY,
product_id INT,
order_date DATE,
quantity INT
);
INSERT INTO products VALUES
(1, 'Widget A', 'Electronics', 100),
(2, 'Widget B', 'Electronics', 150),
(3, 'Gadget C', 'Home', 80);
INSERT INTO order_items (product_id, order_date, quantity) VALUES
(1, '2024-01-01', 10),
(2, '2024-01-02', 5),
(1, '2024-01-03', 8);
-- 4. 使用 WITH NO DATA 创建 DIMV,系统自动创建并补全 mlog
CREATE MATERIALIZED VIEW mv_product_sales
REFRESH FAST ON DEMAND
AS
SELECT p.product_id,
p.category,
SUM(oi.quantity) AS total_qty,
COUNT(*) AS order_count,
MAX(oi.order_date) AS latest_order
FROM products p
JOIN order_items oi ON p.product_id = oi.product_id
GROUP BY p.product_id, p.category
WITH NO DATA;
-- 5. 首次全量刷新,建立增量刷新基线
REFRESH MATERIALIZED VIEW mv_product_sales;
-- 6. 配置每秒自动刷新;mlog 的自动清理间隔由系统一并维护
SELECT polar_matview.set_refresh_interval(
'mv_product_sales'::regclass::oid,
'1 second'::interval
);
-- 7. 后续基表的变更由后台 worker 自动增量刷新到物化视图,并自动清理 mlog
INSERT INTO order_items (product_id, order_date, quantity) VALUES (3, '2024-01-04', 20);
UPDATE order_items SET quantity = quantity + 1 WHERE item_id = 1;
DELETE FROM order_items WHERE item_id = 2;
-- 等待一个刷新周期后查询,即可看到最新结果
SELECT * FROM mv_product_sales ORDER BY product_id;实践建议:
始终使用
WITH NO DATA创建 DIMV,由系统自动创建和补全 mlog。优先使用后台 worker 自动刷新与清理,按业务压力配置 worker 数量;对彼此独立的 DIMV 可由多个 worker 并行处理。
为调度 worker 的角色将
statement_timeout、polar_transaction_timeout等超时参数置 0,避免 worker 被中断。定期通过监控视图(
polar_matview_monitor.dimv_stat_refresh、polar_matview_monitor.mlog_stat_purge)关注 DIMV 与 mlog 的valid状态,及时恢复失效对象。
手动管理
以下为需要精确控制 mlog、刷新或清理时机时的手动操作。常规场景推荐使用WITH NO DATA让系统自动管理。
手动创建与调整 mlog
推荐使用WITH NO DATA创建 DIMV 并让系统自动管理 mlog。如果需要在创建 DIMV 前手动创建 mlog,或者需要为已存在的 mlog 增加/删除列,可使用polar_ivmschema 提供的以下函数。
创建 mlog
不使用WITH NO DATA创建 DIMV 时,需要在创建 DIMV 前手动为每张基表创建 mlog:
SELECT polar_ivm.create_matview_log('base_table'::regclass);补列 / 删列
如果新 DIMV 依赖的列在已有 mlog 中缺失,可调用matview_log_add_column补列;不再需要的列可通过matview_log_drop_column删除:
-- 为 mlog 补列
SELECT polar_ivm.matview_log_add_column('base_table'::regclass, 'new_col');
-- 从 mlog 删列
SELECT polar_ivm.matview_log_drop_column('base_table'::regclass, 'old_col');删除 mlog
T当基表上所有依赖 mlog 的 DIMV 都已删除后,可通过drop_matview_log清理该基表的 mlog:
SELECT polar_ivm.drop_matview_log('base_table'::regclass);基表上还有依赖该 mlog 的 DIMV 时,删除 mlog 会失败。请先删除所有相关 DIMV。
手动刷新
基表发生变更后,调用polar_matview.incremental_refresh_mv()执行按需增量刷新(参数为物化视图 OID):
-- 基表 DML
INSERT INTO users VALUES (1, 'Alice');
UPDATE users SET name = 'Alice A.' WHERE userid = 1;
INSERT INTO sales VALUES (100, 1, '2023-01-01', 500);
-- 增量刷新依赖这两张基表的 DIMV
SELECT polar_matview.incremental_refresh_mv('mv_sales_summary'::regclass::oid);增量刷新有以下事务约束:
必须在
READ COMMITTED隔离级别下执行。最小约束单位是事务。同一事务中,如果某个基表已经执行了会写入 mlog 的 DML,则不能再刷新依赖该表的 DIMV,反过来也一样。
该限制按基表判断。修改无关表或刷新不依赖该基表的 DIMV 不受影响。
可以多次刷新同一个或不同 DIMV,也可以多次修改同一基表。
也可以执行全量刷新。全量刷新会更新增量刷新基线,之后的增量刷新从新基线开始计算:
REFRESH MATERIALIZED VIEW mv_sales_summary;手动清理
通过polar_matview.matview_log_purge()手动清理指定基表的 mlog:
SELECT polar_matview.matview_log_purge('base_table'::regclass);清理只会删除已被所有依赖 DIMV 消费的历史记录。建议先刷新相关 DIMV,再清理 mlog。否则未消费记录不会被清理。
嵌套 DIMV
DIMV 可以引用另一个 DIMV 形成嵌套,分为手动嵌套和自动嵌套两种。
手动嵌套
由您显式地让一个 DIMV 引用另一个 DIMV。示例:先按您聚合销售明细,再筛选出VIP客户:
-- 内层 DIMV:按您聚合销售
CREATE MATERIALIZED VIEW mv_sales_by_user
REFRESH FAST ON DEMAND
AS
SELECT userid,
SUM(amount) AS total_amount,
COUNT(*) AS order_count
FROM sales
GROUP BY userid
WITH NO DATA;
-- 外层 DIMV:引用内层 DIMV 筛选 VIP
CREATE MATERIALIZED VIEW mv_vip_sales
REFRESH FAST ON DEMAND
AS
SELECT u.userid, u.name, s.total_amount
FROM users u
JOIN mv_sales_by_user s ON u.userid = s.userid
WHERE s.total_amount > 10000
WITH NO DATA;
-- 首次全量刷新:必须先内层,再外层
REFRESH MATERIALIZED VIEW mv_sales_by_user;
REFRESH MATERIALIZED VIEW mv_vip_sales;
-- 基表发生变更后,仍然先刷新内层,再刷新外层
SELECT polar_matview.incremental_refresh_mv('mv_sales_by_user'::regclass::oid);
SELECT polar_matview.incremental_refresh_mv('mv_vip_sales'::regclass::oid);使用自动刷新时,同样需要为内层和外层分别设置刷新间隔:刷新外层不会自动带动内层,只为外层设置间隔会让外层读到内层旧数据而保持陈旧。
自动嵌套
使用WITH NO DATA创建DIMV时,系统可以将部分复杂查询拆分为内部DIMV,再由顶层DIMV引用内部结果。典型场景包括:
外连接与聚合组合。
聚合结果参与函数表达式,例如
md5(sum(amount))。子查询中包含聚合、
DISTINCT、GROUP BY或窗口函数的部分场景。
内部DIMV位于polar_autonest_dimv schema,由系统维护,不应直接修改。自动嵌套不覆盖所有复杂SQL;LATERAL外部引用、HAVING,以及DISTINCT与复杂聚合表达式的部分组合会被拒绝。
首次全量刷新时,需要先对polar_autonest_dimv schema中属于该顶层DIMV的内部对象执行REFRESH MATERIALIZED VIEW,再刷新顶层对象;完成首次全量刷新后,内层和顶层对象才会注册到polar_ivm.imatview_metadata。
自动刷新调度器会扫描polar_ivm.imatview_metadata中所有已注册、且刷新间隔不小于0的DIMV,并按各自的间隔独立刷新—刷新顶层DIMV不会自动带动内层,set_refresh_interval也只作用于指定对象、不会向内层级联。因此自动嵌套生成的内部对象同样需要单独设置刷新间隔;只为顶层设置间隔会导致内层得不到刷新,顶层读到内层旧数据而保持陈旧。
分区表作为基表
父表上的DML、新分区以及ATTACH后的DML都通过父表mlog追踪。分区被DETACH后不再写入父表mlog。
需要特别注意的是,ATTACH/DETACH PARTITION这两个操作本身不会写入mlog,因此不会自动更新DIMV的数据:
DETACH一个非空分区时,该分区已有数据对DIMV结果的贡献不会被自动移除。ATTACH一个非空分区时,该分区已有数据也不会被自动计入DIMV结果。
因此建议:
ATTACH时使用空表,ATTACH后再写入的数据会正常通过mlog追踪。DETACH后对相关DIMV执行一次全量刷新以剔除被移除分区的贡献。如果业务层面可以容忍、或希望保留被DETACH分区的历史贡献,也可以不处理。
监控视图
DIMV 刷新统计
通过监控视图polar_matview_monitor.dimv_stat_refresh查看DIMV刷新执行情况:
SELECT * FROM polar_matview_monitor.dimv_stat_refresh;各字段含义如下:
字段 | 含义 |
| DIMV名称。 |
| DIMV的OID。 |
| 增量刷新累计次数。 |
| 增量刷新平均耗时。 |
| 增量刷新最大耗时。 |
| 增量刷新最小耗时。 |
| 增量刷新累计耗时。 |
| 最近一次增量刷新的开始时间。 |
| 最近一次增量刷新的结束时间。 |
| 数据延迟,即当前时间与上次刷新时间之差。 |
| 事务延迟,即上次刷新基线事务ID的年龄。 |
| 该DIMV是否仍可增量刷新,为 |
mlog 清理统计
通过监控视图polar_matview_monitor.mlog_stat_purge查看mlog清理执行情况:
SELECT * FROM polar_matview_monitor.mlog_stat_purge;各字段含义如下:
字段 | 含义 |
| mlog对应的基表名称。 |
| 基表的OID。 |
| 清理累计次数。 |
| 清理平均耗时。 |
| 清理最大耗时。 |
| 清理最小耗时。 |
| 清理累计耗时。 |
| 单次清理平均行数。 |
| 单次清理最大行数。 |
| 单次清理最小行数。 |
| 累计清理行数。 |
| 最近一次清理后mlog的剩余行数。 |
| 最近一次清理的开始时间。 |
| 最近一次清理的结束时间。 |
| mlog中最老事务ID的年龄,接近 |
| 距离因清理超时而失效的剩余时长。 |
| mlog是否有效。 |
失效与恢复
mlog 失效与恢复
当满足以下任一条件时,mlog会被标记为失效,依赖它的DIMV在增量刷新(polar_matview.incremental_refresh_mv())时会收到警告并返回false:
mlog的事务ID跨度超过
polar_mlog_xid_max_limit(默认20亿)。事务ID接近回卷时,日志记录的新旧顺序无法可靠判断。距上次清理的时间超过
polar_mlog_purge_max_interval(默认18000秒)。长时间未清理时,timestamp维度的兜底判断也无法继续保证日志可识别。
正常情况下不会触发这两个条件。只要mlog被worker持续清理,其事务ID跨度就会随清理不断收窄,不会逼近20亿。因此这两个条件实际上只是一道兜底保护:通常只有在放弃自动清理、改用手动purge,且长时间疏于清理(这段时间内消费的事务ID已超过20亿)的情况下才可能被触发。
可通过监控视图polar_matview_monitor.mlog_stat_purge的valid、age、valid_lifecycle字段检测mlog状态。
恢复方式是清空并重新验证mlog,再对依赖它的DIMV重建基线:
SELECT polar_ivm.matview_log_truncate_and_validate('base_table_name'::regclass);DIMV 失效与恢复
DIMV失效指物化视图本身变为不可增量刷新(元数据polar_ivm.imatview_metadata.is_incrementally_updatable置为false),主要由以下操作触发:
对基表执行
TRUNCATE:会使依赖该基表且已填充数据的DIMV全部失效。对作为其他DIMV基表的嵌套DIMV执行非并发的全量
REFRESH MATERIALIZED VIEW:会重置其mlog并级联使下游DIMV失效。基表mlog被重置(如上文
polar_ivm.matview_log_truncate_and_validate()):级联使依赖该基表的DIMV失效。
其他会破坏增量基线的不兼容DDL(如删除、修改被依赖的列)默认会被直接阻止,而非使DIMV静默失效,具体请参见基表 DDL 操作的影响。
基表 DDL 操作的影响
DIMV基表上的DDL会受到约束,以保护增量刷新基线。常见操作的行为如下:
DDL 操作 | 行为 |
| 允许;新DIMV使用前需要将列加入mlog, |
| 被DIMV或mlog依赖时阻止; |
| 允许;依赖定义会同步更新。 |
| 被DIMV或mlog依赖时阻止。 |
| 允许;依赖定义会同步更新。 |
| 允许;DIMV会变为不可增量更新,需要全量刷新恢复。 |
参数说明
参数名 | 类型 | 默认值 | 说明 |
| bool |
| 是否自动将基表索引复制到mlog。 |
| enum |
| 是否启用DIMV刷新与mlog清理的统计,取值 |
| int | 2000000000 | mlog事务ID跨度上限,超过后mlog失效。默认20亿。 |
| int | 18000 | mlog距上次清理的最大时长(秒),超过后mlog失效。 |
附录:支持的查询类型
特性 | 是否支持 | 说明 |
简单 | 支持 | 支持投影、过滤和表达式。 |
内连接 | 支持 | 支持多表连接和自连接。 |
外连接 | 部分支持 | 可通过自动嵌套支持部分场景,复杂组合有约束。 |
| 支持 | 支持常见表达式和部分子查询场景。 |
| 支持 | 支持单列、多列和NULL分组键。 |
常见聚合 | 支持 |
|
任意聚合 | 支持 | 必须按 |
顶层 | 支持 | 子查询中的 |
子查询 | 部分支持 | 支持部分简单子查询、 |
分区表作为基表 | 支持 | mlog建在分区父表上。 |
分区物化视图 | 支持 | 通过辅助函数创建父表和分区物化视图。 |
窗口函数 | 部分支持 | 必须有 |
| 不支持 | DIMV定义不能包含 |
| 不支持 | 不支持集合操作。 |
| 不支持 | 不支持结果裁剪。 |
可变函数 | 不支持 | 例如 |
复杂CTE、嵌套EXISTS、LATERAL外部引用,以及聚合、GROUP BY、DISTINCT与EXISTS的组合存在限制。应先在测试库验证具体SQL。
支持的聚合函数
除常见聚合(SUM、AVG、COUNT、MIN、MAX)外,DIMV 还支持以下聚合:
字符串/数组聚合:
string_agg、array_agg。有序集聚合:如
percentile_disc、percentile_cont等。您自定义聚合:您通过
CREATE AGGREGATE定义的聚合函数。
示例:使用string_agg和array_agg:
CREATE MATERIALIZED VIEW mv_agg
REFRESH FAST ON DEMAND
AS
SELECT cat,
string_agg(name, ',') AS names,
array_agg(id ORDER BY id) AS ids
FROM t_dimv
GROUP BY cat
WITH NO DATA;任意聚合的限制
必须带
GROUP BY,不支持没有GROUP BY的全表任意聚合。支持NULL分组键和多列分组键。
发生变更的group会整体重算,刷新复杂度约为
O(delta group)。常见聚合可按基表变化粒度刷新,通常比任意聚合路径轻量。
窗口函数的限制
PolarDB PostgreSQL版当前支持的窗口函数包括ROW_NUMBER()、RANK()、DENSE_RANK()、LEAD()、LAG()、FIRST_VALUE()、LAST_VALUE()、NTH_VALUE()和窗口聚合。限制如下:
每个窗口函数都必须指定
PARTITION BY,不支持全表窗口。多个窗口函数的
PARTITION BYkey必须形成兼容的子集链。PARTITION BY可以使用列或不可变函数表达式。窗口函数不能与
GROUP BY或聚合函数组合。窗口函数不能与外连接或
EXISTS子链接组合。窗口函数不能位于子查询中。