本文介紹 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。
