本文介绍 Hologres Dynamic Table 的查询改写能力、使用方式与限制。
查询改写介绍
在大数据/数仓场景中,明细表数据量通常很大(亿级、百亿级),而业务与分析查询又高度依赖在明细表上进行多维 GROUP BY + 聚合,例如:按天 / 小时 / 城市统计订单量、GMV;按渠道 / 终端统计 PV/UV、转化率等。如果每次都直接在明细表上做聚合,会遇到如下问题:
聚合成本高:每次查询都从明细表全表或大范围扫描并聚合,消耗大量 CPU 和 IO;
明细表压力大:影响同库内其他任务,或需要频繁扩容。
Hologres 提供 Dynamic Table + 查询改写能力:当某张基表上已经通过 Dynamic Table 做了预聚合,优化器可以在满足条件时,将用户写的“面向基表的聚合查询”自动改写为“对Dynamic Table 的查询”,从而跳过昂贵的聚合计算。主要收益有:
减轻明细表聚合压力:高频指标(订单数、GMV、PV/UV 等)可从 Dynamic Table 直接读取聚合结果,减少对明细表的重复扫描和聚合。
显著提升查询响应速度:报表、自助分析、交互式查询在命中 Dynamic Table 时,聚合计算大幅减少,延迟降低,使用体验接近“查宽表”。
对上层使用无侵入:数据分析师、业务开发依然只需了解基表模型;Dynamic Table 的设计与维护由数仓/平台团队统一负责;性能优化对上层透明。
通过Hologres Dynamic Table查询改写,可以很好的支持如下场景:
实时/准实时运营看板和监控;
多维度 BI 分析、自助取数;
核心指标体系(GMV、订单量、活跃用户数等)的统一口径加速。
使用与限制
版本限制:仅Hologres 4.1及以上版本支持该功能
查询一致性限制:查询改写基于 Dynamic Table 最近一次刷新结果,与基表最新状态相比,存在一定时间窗口的延迟,属于弱一致行为。
基表类型限制:
支持的基表类型有:Hologres内表、Paimon外表(Foreign Table方式创建)、MaxCompute外表(Foreign Table方式创建)
如果基表是Hologres分区表,不支持基表为物理分区表。但基表可以是逻辑分区
不支持External Table
Dynamic Table表类型限制:
支持:非分区 Dynamic Table;逻辑分区的 Dynamic Table;
不支持:物理分区的 Dynamic Table;External Dynamic Table。
Dynamic Table中Query 定义限制:
Query中仅支持单表,暂不支持多表
Query中不支持
FILTER子句的聚合(如sum(x) FILTER (WHERE ...))Query中不支持在 SELECT 中引入基于聚合结果的额外计算列(如
sum(x)/count(x))
开启与配置查询改写
使用建议:适合看板、监控、分析等可接受秒级~分钟级延迟的场景;强实时、严对账场景建议直接查基表或采用其他强一致方案。
开启查询改写
当发起对基表的查询时,需要在查询时,加上hg_enable_query_rewriteGUC参数,来控制该查询是否命中查询改写:
不建议在 DB 级别开启,可能带来性能损失。
-- 开启查询改写(Session 级别)
SET hg_enable_query_rewrite = on;
-- DB 级别设置(不推荐)
ALTER DATABASE <db_name> SET hg_enable_query_rewrite = on;为 Dynamic Table 开启查询改写
创建 Dynamic Table 时,通过属性 allowed_to_rewrite_query 控制该表是否参与查询改写;未设置时,默认不参与改写。
CREATE [ OR REPLACE ] DYNAMIC TABLE [ IF NOT EXISTS ] [<schema_name>.]<table_name> (
[col_name],
[col_name],
[col_name]
)
[LOGICAL PARTITION BY LIST(<partition_key>)]
WITH (
...,
allowed_to_rewrite_query = '[true | false]',
...
)
AS
<query>;参数说明:
allowed_to_rewrite_query:标记此 Dynamic Table 是否允许作为“查询改写候选”;'true':允许被查询改写使用;'false':默认值,不参与查询改写;
使用建议:
专门用于加速聚合查询的 DT:设置为
'true';定义复杂且当前规则无法利用的 DT:设置为
'false',减少优化器无效探索。
修改 Dynamic Table 查询改写属性
可通过 ALTER DYNAMIC TABLE ... SET 修改是否参与查询改写:
ALTER DYNAMIC TABLE [IF EXISTS] [<schema_name>.]<table_name>
SET (allowed_to_rewrite_query = '[true | false]');控制查询改写命中的 Dynamic Table
存在多个 Dynamic Table 时,可在 Hint 中指定候选表集合,缩小优化器搜索范围并控制优先级。Hint 用法见 HINT。
SELECT /*+HINT query_rewrite_candidates(<schema.dt_name1> <schema.dt_name2> ...) */
...
FROM ...;GUC使用说明:
若有多个 Dynamic Table,需使用空格分隔;
可指定 schema。
使用示例:
-- 仅允许 dt_sales 参与查询改写
SELECT /*+HINT query_rewrite_candidates(dt_sales) */
day, hour, min(amount), max(amount)
FROM base_sales_table
GROUP BY day, hour;支持的功能介绍
当前版本支持基于单表聚合的查询改写,主要包括三种模式:
聚合维度一致的透明改写;
聚合上卷(Group By 维度上卷聚合);
条件补偿的聚合上卷(带过滤条件的聚合上卷)。
聚合维度一致
适用条件:
查询中的
GROUP BY维度与 Dynamic Table 定义中的GROUP BY维度完全一致;查询使用的聚合函数可以由Dynamic Table中已有的聚合结果列直接表示;
聚合函数类型本身不限(包括 DISTINCT),只要Dynamic Table中已经存在对应结果列。
使用示例:
--创建基表
CREATE TABLE base_sales_table(
day text not null,
hour int,
amount int
);
--写入数据
INSERT INTO base_sales_table
VALUES ('20250529', 12, 1),
('20250529', 12, 2),
('20250529', 12, 2),
('20250529', 13, 3),
('20250530', 13, 4),
('20250530', 14, 5),
('20250531', 14, 6);
-- Dynamic table
CREATE DYNAMIC TABLE dt_sales
WITH (
freshness = '1 minutes',
auto_refresh_mode='incremental',
auto_refresh_enable='false',
allowed_to_rewrite_query='true'
)
AS
SELECT
day,
hour,
min(amount),
max(amount),
sum(amount),
count(amount),
count(*) as rows,
count(1) as rows1,
count(distinct amount) as cd
FROM base_sales_table
GROUP BY day, hour;
REFRESH TABLE dt_sales;查询示例:维度一致时,执行计划中可见基表查询被改写为对 Dynamic Table 的查询。
-- 查询维度一致
EXPLAIN SELECT day, hour, min(amount), max(amount) FROM base_sales_table GROUP BY day, hour;
聚合上卷
适用条件:
Dynamic Table 的
GROUP BY维度为查询GROUP BY维度的超集(即 DT 的维度包含查询维度);查询中的聚合函数可以通过对 DT 已有聚合结果列再聚合得到;
支持的聚合函数:
min,max,count,sum,avg;不支持 DISTINCT 聚合上卷(但维度一致场景直接用 DT 中结果列除外)。
聚合函数映射:
原始查询基表的聚合函数 | Dynamic Table 中需存在的聚合列 | 改写后聚合函数 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
使用示例:
--创建基表
CREATE TABLE base_sales_table(
day text not null,
hour int,
amount int
);
--写入数据
INSERT INTO base_sales_table
VALUES ('20250529', 12, 1),
('20250529', 12, 2),
('20250529', 12, 2),
('20250529', 13, 3),
('20250530', 13, 4),
('20250530', 14, 5),
('20250531', 14, 6);
-- Dynamic table:为了验证查询改写的效果,先手动关闭自动刷新
CREATE DYNAMIC TABLE dt_sales
WITH (
freshness = '1 minutes',
auto_refresh_mode='incremental',
auto_refresh_enable='false',
allowed_to_rewrite_query='true'
)
AS
SELECT
day,
hour,
min(amount),
max(amount),
sum(amount),
count(amount),
count(*) as rows,
count(1) as rows1,
count(distinct amount) as cd
FROM base_sales_table
GROUP BY day, hour;
--手动刷新dynamic table
REFRESH TABLE dt_sales;查询示例 1:按 day 聚合(上卷)。查询基表时,GROUP BY 列为 Dynamic Table 定义中的子集,可被正常改写。
-- 原始查询
EXPLAIN SELECT day, min(amount), max(amount)
FROM base_sales_table
GROUP BY day;
查询示例 2:sum + count + avg 上卷。基表查询使用 avg,Dynamic Table 中有 sum 和 count,可推导出 avg,因此可被改写。
-- 原始查询
EXPLAIN SELECT day, sum(amount), count(amount), avg(amount)
FROM base_sales_table
GROUP BY day;
条件补偿的聚合上卷(带过滤条件)
适用条件:
查询基表时包含
WHERE过滤条件,但是Dynamic Table定义中不能有where过滤条件所有用于where过滤的字段都需要出现在 Dynamic Table 的
GROUP BY维度中;聚合函数仅支持
min,max,count,sum,avg,不支持 DISTINCT。
使用示例:
--创建基表
CREATE TABLE base_sales_table(
day text not null,
hour int,
amount int
);
--写入数据
INSERT INTO base_sales_table
VALUES ('20250529', 12, 1),
('20250529', 12, 2),
('20250529', 12, 2),
('20250529', 13, 3),
('20250530', 13, 4),
('20250530', 14, 5),
('20250531', 14, 6);
-- Dynamic table:为了验证查询改写的效果,先手动关闭自动刷新
CREATE DYNAMIC TABLE dt_sales
WITH (
freshness = '1 minutes',
auto_refresh_mode='incremental',
auto_refresh_enable='false',
allowed_to_rewrite_query='true'
)
AS
SELECT
day,
hour,
min(amount),
max(amount),
sum(amount),
count(amount),
count(*) as rows,
count(1) as rows1,
count(distinct amount) as cd
FROM base_sales_table
GROUP BY day, hour;
--手动刷新dynamic table
REFRESH TABLE dt_sales;以下示例中,查询基表时带有 WHERE 条件,且过滤字段均出现在 GROUP BY 中,因此可被改写。
EXPLAIN SELECT day, sum(amount), count(amount), avg(amount)
FROM base_sales_table
WHERE day > '20250528' AND day <= '20250531'
GROUP BY day;
查看查询改写情况
开启查询改写后,可通过以下方式确认当前查询是否命中了 Dynamic Table。
通过执行计划查看:在 EXPLAIN 结果中查看 Scan 算子扫描的表名,可判断是否命中了 Dynamic Table。
通过慢 query 日志查看改写情况:在慢 query 日志
hologres.hg_query_log表的extended_info字段中会记录查询改写命中的表;若改写失败,会包含相关错误说明。
select extended_info::json->>'rewrite_query_info' from hologres.hg_query_log where query_id = 'xxxxx';
?column?
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
{"rewrite_failed_dt": [[\"public.dt3\", {\"rewrite_failed_cause\": \"Doesn't include all query required output columns\"}]], \"rewrite_succeeded_and_selected_dt\": [\"public.dt2\"], \"rewrite_succeeded_but_not_selected_dt\": [\"public.dt1\"]}
(1 row)使用示例
示例 1:基表为 Hologres 内表的查询改写
基表来自 TPC-H 数据集 lineitem 100G,建表与导入方法见 一键导入公共数据集。本示例中 Dynamic Table 为增量刷新、非分区表。
CREATE DYNAMIC TABLE dt_lineitem_100g_incremental
WITH (
freshness = '10 minutes',
auto_refresh_mode='incremental',
auto_refresh_enable='false',
allowed_to_rewrite_query='true')
AS
select
l_returnflag,
l_linestatus,
l_shipdate,
sum(l_quantity) as sum_qty,
count(*) as count_order
from
hologres_dataset_tpch_100g.lineitem
group by
l_returnflag,
l_linestatus,
l_shipdate
--手动刷新
REFRESH DYNAMIC TABLE dt_lineitem_100g_incremental;查询基表:
set hg_enable_query_rewrite = on;
explain
select
l_returnflag,
l_linestatus,
l_shipdate,
sum(l_quantity) as sum_qty,
count(*) as count_order
from
hologres_dataset_tpch_100g.lineitem
where l_shipdate = '1998-12-01'
group by
l_returnflag,
l_linestatus,
l_shipdate执行计划中可以看到查询被改写为查询DT:

查询基表的结果如下:
l_returnflag | l_linestatus | l_shipdate | sum_qty | count_order
--------------+--------------+------------+----------+-------------
N | O | 1998-12-01 | 52841.00 | 2070
(1 row)直接查询 Dynamic Table(本示例关闭了自动刷新,仅做了一次手动刷新),结果与最近一次刷新一致:
select
l_returnflag,
l_linestatus,
l_shipdate,
sum_qty,
count_order
from
dt_lineitem_100g_incremental
where l_shipdate = '1998-12-01' ;
l_returnflag | l_linestatus | l_shipdate | sum_qty | count_order
--------------+--------------+------------+----------+-------------
N | O | 1998-12-01 | 52841.00 | 2070
(1 row)示例 2:基表是 Paimon 外表的查询改写
当基表为 Paimon 外表时,同样可命中查询改写。示例步骤如下:
准备 Paimon 表:示例在 Paimon 中导入 TPC-H customer 100G 表,导入方法见 Paimon Table。
在 Hologres 中创建 Paimon 外表:需使用 Foreign Table 方式创建,详见 基于DLF访问Paimon Catalog。
-- 创建 foreign server
CREATE SERVER IF NOT EXISTS paimon_server FOREIGN DATA WRAPPER dlf_fdw OPTIONS (
catalog_type 'paimon',
metastore_type 'dlf-rest',
dlf_catalog '<dlf_catalog_name>'
);
-- 使用IMPORT FOREIGN SCHEMA创建paimon外表
IMPORT FOREIGN SCHEMA <schema_name>
limit to (customer)
FROM SERVER paimon_server into public
options (if_table_exist 'update');
--查询数据
SELECT * FROM customer创建 Dynamic Table 增量消费 Paimon 外表:在 Hologres 中创建 Dynamic Table,以增量刷新方式消费 Paimon 外表;为便于验证改写效果,本示例关闭自动刷新。
--创建dynamic table
CREATE DYNAMIC TABLE dt_paimon_customer
WITH (
freshness = '10 minutes',
auto_refresh_mode='incremental',
auto_refresh_enable='false',
allowed_to_rewrite_query='true')
AS
SELECT
c_custkey,
avg(c_acctbal) ,
sum(c_acctbal) ,
count(c_acctbal)
FROM customer
group by c_custkey;
--手动刷新dynamic table
REFRESH DYNAMIC TABLE dt_paimon_customer;4、查询 Paimon 外表并开启查询改写。
set hg_enable_query_rewrite = on;
SELECT
c_custkey,
avg(c_acctbal) ,
sum(c_acctbal) ,
count(c_acctbal)
FROM
customer
group by c_custkey ORDER BY 3 DESC LIMIT 3;
c_custkey | avg |sum |count
----------|-------------|---------|-----
3605586 |9999.990000 | 9999.99 |1
10705496 |9999.990000 |9999.99 |1
14959900 |9999.990000 |9999.99 |1查询 Dynamic Table:结果为最近一次刷新后的数据。
SELECT * FROM dt_paimon_customer ORDER BY 3 DESC LIMIT 3;
c_custkey | avg |sum |count
----------|-------------|---------|-----
3605586 |9999.990000 | 9999.99 |1
10705496 |9999.990000 |9999.99 |1
14959900 |9999.990000 |9999.99 |1通过执行计划确认:可看到查询已改写为访问 Dynamic Table。
