增量物化视图

更新时间:
复制 MD 格式

PolarDB PostgreSQL提供了增量物化视图(也称按需增量物化视图,DIMV)功能,通过物化视图日志(Materialized View Log,简称mlog)异步追踪基表的数据变更,在刷新时仅应用增量数据(delta),避免全量重算,从而提升物化视图的刷新效率。

背景介绍

物化视图本质上是把一条查询Q的结果预先计算并存储下来。普通物化视图在刷新时执行的是REFRESH MATERIALIZED VIEW,它会重新完整执行一遍Q:扫描所有基表、重新做连接和聚合,再用结果整体替换旧数据。当基表很大、但两次刷新之间只改动了少量行时,这种“全量重算”把绝大部分时间花在了重复计算没有变化的数据上。

增量物化视图换了一个思路:物化视图的结果是基表的函数,记作V = Q(基表)。如果能知道基表自上次刷新以来发生的变化量Δ(哪些行被插入、删除、更新),那么视图结果的变化量ΔV也只由Δ决定,而与那些没有变化的海量数据无关。于是刷新就从“重算整个V”变成“算出ΔV并把它合并进已有的V”—计算量正比于改动量,而不是基表总量。

实现这一思路需要解决三个问题,它们分别对应以下三个核心概念:

  1. 如何捕获基表的变更:在基表上创建触发器,将每次INSERT/UPDATE/DELETE产生的变化行记录到一张日志表中,该表即物化视图日志(mlog),用于承载上文所述的Δ

  2. 如何将变更应用到视图:刷新时读取自上次刷新以来积累的mlog(即该时间段内的Δ),据此计算ΔV并合并进物化视图。由于仅在您主动刷新时执行、且只处理增量,故称为按需增量刷新。

  3. 如何回收已消费的日志:mlog随基表变更持续增长,当一条记录已被所有依赖它的物化视图刷新消费后即可删除,此过程称为日志清理(purge),用于防止日志无限膨胀。

功能概述

  • 物化视图日志(mlog):在基表上创建mlog表,通过触发器记录INSERTUPDATEDELETE产生的变更。

  • 按需增量刷新:刷新时读取自上次刷新以来的mlog数据并将其应用到物化视图。

  • 日志清理(purge):清理已被所有依赖物化视图消费的mlog条目,避免日志持续增长。

适用范围

增量物化视图(DIMV)依赖polar_ivm插件。使用前请确认集群内核版本、扩展安装情况满足以下要求。

  • 内核版本PostgreSQL 14,且内核小版本需为2.0.14.20.46.0及以上版本。

    说明

    您可在控制台查看内核小版本号,也可以通过SHOW polardb_version;语句查看。如未满足内核小版本要求,请升级内核小版本

  • 安装 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_timeoutpolar_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中所有已注册、且刷新间隔不小于0DIMV,并按各自的间隔独立刷新。刷新是逐对象进行的,不会自动带动其依赖的其他DIMV。嵌套场景下需要为每一层分别设置间隔。

如无特殊原因,建议将刷新间隔设置为秒级而非分钟级。增量刷新的CPU开销只与增量数据量相关,而与刷新频率无关:拉长间隔并不会减少总开销,只会让变更在间隔内堆积,刷新时形成资源波峰。间隔越小,负载越平滑,波峰波谷被展平。

需要注意的是每次刷新自身有加锁等固定overhead,间隔过小会让这部分固定开销占比上升。因此将间隔设为0(尽快刷新)可能反而增大总体资源开销,仅在对刷新延迟有极致要求时才推荐使用。

某个基表被多个DIMV直接依赖时,该基表mlog的自动清理间隔取这些DIMV刷新间隔的最大值;所有直接依赖者都关闭自动刷新后,该基表mlog的自动清理也会关闭。

调度器只处理仍可增量更新的DIMV和有效mlog。DIMVmlog失效时,需要先执行恢复操作。

完整示例

下面是一个从安装扩展到配置自动刷新的端到端示例,覆盖 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_timeoutpolar_transaction_timeout等超时参数置 0,避免 worker 被中断。

  • 定期通过监控视图(polar_matview_monitor.dimv_stat_refreshpolar_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))

  • 子查询中包含聚合、DISTINCTGROUP 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中所有已注册、且刷新间隔不小于0DIMV,并按各自的间隔独立刷新—刷新顶层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_name

DIMV名称。

dimv_oid

DIMVOID。

calls

增量刷新累计次数。

mean_time

增量刷新平均耗时。

max_time

增量刷新最大耗时。

min_time

增量刷新最小耗时。

total_time

增量刷新累计耗时。

last_start_time

最近一次增量刷新的开始时间。

last_end_time

最近一次增量刷新的结束时间。

data_delay

数据延迟,即当前时间与上次刷新时间之差。

xid_delay

事务延迟,即上次刷新基线事务ID的年龄。

valid

DIMV是否仍可增量刷新,为false时需参考失效与恢复处理。

mlog 清理统计

通过监控视图polar_matview_monitor.mlog_stat_purge查看mlog清理执行情况:

SELECT * FROM polar_matview_monitor.mlog_stat_purge;

各字段含义如下:

字段

含义

relname

mlog对应的基表名称。

relid

基表的OID。

calls

清理累计次数。

mean_time

清理平均耗时。

max_time

清理最大耗时。

min_time

清理最小耗时。

total_time

清理累计耗时。

mean_rows

单次清理平均行数。

max_rows

单次清理最大行数。

min_rows

单次清理最小行数。

total_rows

累计清理行数。

last_remain_rows

最近一次清理后mlog的剩余行数。

last_start_time

最近一次清理的开始时间。

last_end_time

最近一次清理的结束时间。

age

mlog中最老事务ID的年龄,接近polar_mlog_xid_max_limit时趋于失效。

valid_lifecycle

距离因清理超时而失效的剩余时长。

valid

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维度的兜底判断也无法继续保证日志可识别。

正常情况下不会触发这两个条件。只要mlogworker持续清理,其事务ID跨度就会随清理不断收窄,不会逼近20亿。因此这两个条件实际上只是一道兜底保护:通常只有在放弃自动清理、改用手动purge,且长时间疏于清理(这段时间内消费的事务ID已超过20亿)的情况下才可能被触发。

可通过监控视图polar_matview_monitor.mlog_stat_purgevalidagevalid_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 操作

行为

ALTER TABLE ADD COLUMN

允许;新DIMV使用前需要将列加入mlog,WITH NO DATA可自动补列。

ALTER TABLE DROP COLUMN

DIMVmlog依赖时阻止;CASCADE可级联删除依赖。

ALTER TABLE RENAME COLUMN

允许;依赖定义会同步更新。

ALTER TABLE ALTER COLUMN TYPE

DIMVmlog依赖时阻止。

ALTER TABLE RENAME

允许;依赖定义会同步更新。

TRUNCATE

允许;DIMV会变为不可增量更新,需要全量刷新恢复。

参数说明

参数名

类型

默认值

说明

polar_auto_create_mlog_index

bool

on

是否自动将基表索引复制到mlog。

polar_ivm_stat_track

enum

enable

是否启用DIMV刷新与mlog清理的统计,取值enabledisable

polar_mlog_xid_max_limit

int

2000000000

mlog事务ID跨度上限,超过后mlog失效。默认20亿。

polar_mlog_purge_max_interval

int

18000

mlog距上次清理的最大时长(秒),超过后mlog失效。

附录:支持的查询类型

特性

是否支持

说明

简单SELECT

支持

支持投影、过滤和表达式。

内连接

支持

支持多表连接和自连接。

外连接

部分支持

可通过自动嵌套支持部分场景,复杂组合有约束。

WHERE

支持

支持常见表达式和部分子查询场景。

GROUP BY

支持

支持单列、多列和NULL分组键。

常见聚合

支持

SUMAVGCOUNTMINMAX

任意聚合

支持

必须按GROUP BY分组维护。

顶层DISTINCT

支持

子查询中的DISTINCT仅部分场景可通过自动嵌套支持。

子查询

部分支持

支持部分简单子查询、EXISTSIN和简单CTE。

分区表作为基表

支持

mlog建在分区父表上。

分区物化视图

支持

通过辅助函数创建父表和分区物化视图。

窗口函数

部分支持

必须有PARTITION BY,并受下文组合限制。

HAVING

不支持

DIMV定义不能包含HAVING

UNION / INTERSECT / EXCEPT

不支持

不支持集合操作。

LIMIT / OFFSET

不支持

不支持结果裁剪。

可变函数

不支持

例如random()now()

复杂CTE、嵌套EXISTSLATERAL外部引用,以及聚合、GROUP BYDISTINCTEXISTS的组合存在限制。应先在测试库验证具体SQL。

支持的聚合函数

除常见聚合(SUMAVGCOUNTMINMAX)外,DIMV 还支持以下聚合:

  • 字符串/数组聚合string_aggarray_agg

  • 有序集聚合:如percentile_discpercentile_cont等。

  • 您自定义聚合:您通过CREATE AGGREGATE定义的聚合函数。

示例:使用string_aggarray_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 BY key必须形成兼容的子集链。

  • PARTITION BY可以使用列或不可变函数表达式。

  • 窗口函数不能与GROUP BY或聚合函数组合。

  • 窗口函数不能与外连接或EXISTS子链接组合。

  • 窗口函数不能位于子查询中。