@clickzetta/cz-cli-darwin-x64 0.3.10 → 0.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cz-cli CHANGED
Binary file
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: cz-cli
3
- description: "Route ALL ClickZetta Lakehouse operations to cz-cli: SQL, Studio tasks, tables, pipelines, profiles. Never for general programming or non-ClickZetta work."
2
+ name: cz-cli-v2
3
+ description: Route ALL ClickZetta Lakehouse operations to cz-cli: SQL, Studio tasks, tables, pipelines, profiles. Use when user mentions ClickZetta, Lakehouse, cz-cli, or needs profile/connection configuration.
4
4
  ---
5
5
 
6
6
  # cz-cli — ClickZetta Lakehouse Subagent
@@ -47,6 +47,71 @@ cz-cli agent run "<request>" --profile uat_test --format a2a --dangerously-skip-
47
47
 
48
48
  Available profiles: read `~/.clickzetta/profiles.toml` or run `cz-cli profile list`.
49
49
 
50
+ ## Adding a new profile
51
+
52
+ **Trigger conditions:** User says "configure new environment", "add profile", "can't connect", mentions an unknown profile name, or provides connection credentials.
53
+
54
+ ### Step 1 — Collect information (guided Q&A)
55
+
56
+ If all required fields are already provided, skip directly to Step 2.
57
+
58
+ Otherwise, ask for missing ones. Accept all at once or prompt one by one.
59
+
60
+ **Required fields:**
61
+
62
+ | Field | Question to ask | Example |
63
+ |-------|----------------|---------|
64
+ | `service` | Which cloud region? (see table below, or provide the service endpoint directly) | `cn-shanghai-alicloud.api.clickzetta.com` |
65
+ | `instance` | What is the instance name? | `billingsh` |
66
+ | `workspace` | What is the workspace name? | `meter_n_bill` |
67
+ | `username` | What is the username? | `billing_admin` |
68
+ | `password` | What is the password? | — |
69
+ | `name` | What should this profile be named? (suggested format below) | `billingsh` |
70
+
71
+ **Common service endpoints (offer as options):**
72
+
73
+ | Region | service | Suggested profile prefix |
74
+ |--------|---------|--------------------------|
75
+ | Alibaba Cloud East China 2 (Shanghai) | `cn-shanghai-alicloud.api.clickzetta.com` | `cn-shanghai` |
76
+ | Tencent Cloud East China (Shanghai) | `ap-shanghai-tencentcloud.api.clickzetta.com` | `ap-shanghai` |
77
+ | Tencent Cloud North China (Beijing) | `ap-beijing-tencentcloud.api.clickzetta.com` | `ap-beijing` |
78
+ | Tencent Cloud South China (Guangzhou) | `ap-guangzhou-tencentcloud.api.clickzetta.com` | `ap-guangzhou` |
79
+ | AWS China (Beijing) | `cn-north-1-aws.api.clickzetta.com` | `cn-north-1` |
80
+
81
+ **Inference rules (reduce unnecessary questions):**
82
+ - If the user describes a cloud region in natural language (e.g. "Alibaba Cloud Shanghai", "Tencent Cloud Beijing", "阿里云上海", "腾讯云北京"), look up the service endpoint from the table above — do NOT ask the user to provide it again.
83
+ - If the user hasn't provided a profile name, suggest `<prefix>-<instance>` using the prefix from the table (e.g. `cn-shanghai-billingsh`). Confirm with the user or proceed if they don't object.
84
+
85
+ ### Step 2 — Create profile
86
+
87
+ Run `cz-cli profile create` with all collected fields:
88
+
89
+ ```bash
90
+ cz-cli profile create <name> \
91
+ --username <username> \
92
+ --password <password> \
93
+ --instance <instance> \
94
+ --workspace <workspace> \
95
+ --service <service> \
96
+ --schema public \
97
+ --vcluster default
98
+ ```
99
+
100
+ ### Step 3 — Verify connection
101
+
102
+ After creating, run:
103
+
104
+ ```bash
105
+ cz-cli status --profile <name>
106
+ ```
107
+
108
+ A successful response looks like:
109
+ ```json
110
+ {"data": {"connected": true, "workspace": "...", "time_ms": ...}}
111
+ ```
112
+
113
+ If it fails, report the error and ask the user to double-check credentials or service endpoint.
114
+
50
115
  ## Error handling
51
116
 
52
117
  All errors in non-TTY mode output JSON to stdout:
@@ -55,4 +120,4 @@ All errors in non-TTY mode output JSON to stdout:
55
120
  {"ok": false, "error": "NO_PROFILE", "next_steps": ["cz-cli setup --credential <base64>"]}
56
121
  ```
57
122
 
58
- On `NO_PROFILE` error: guide user to run `cz-cli setup --credential <base64>`. See `references/profile-setup.md`.
123
+ On `NO_PROFILE` error: check if a profile can be configured via username/password (see "Adding a new profile" above). If the user has a base64 credential instead, guide them to run `cz-cli setup --credential <base64>`. See `references/profile-setup.md`.
@@ -79,6 +79,38 @@ For `openai-compatible` (third-party relays), add `--base-url <url>`.
79
79
  "supported_providers": ["anthropic", "openai", "openai-compatible", "bedrock", "google", "azure"]}
80
80
  ```
81
81
 
82
+ ## Alternative: username/password profile (no credential required)
83
+
84
+ If the user has an existing instance account (username + password) but no base64 credential, use `cz-cli profile create` directly:
85
+
86
+ ```bash
87
+ cz-cli profile create <name> \
88
+ --username <username> \
89
+ --password <password> \
90
+ --instance <instance_name> \
91
+ --workspace <workspace_name> \
92
+ --service <service_host> \
93
+ --schema public \
94
+ --vcluster default
95
+ ```
96
+
97
+ **Common service endpoints:**
98
+
99
+ | 云区域 | service |
100
+ |--------|---------|
101
+ | 阿里云 华东2(上海) | `cn-shanghai-alicloud.api.clickzetta.com` |
102
+ | 腾讯云 华东(上海) | `ap-shanghai-tencentcloud.api.clickzetta.com` |
103
+ | 腾讯云 华北(北京) | `ap-beijing-tencentcloud.api.clickzetta.com` |
104
+ | 腾讯云 华南(广州) | `ap-guangzhou-tencentcloud.api.clickzetta.com` |
105
+ | AWS 中国(北京) | `cn-north-1-aws.api.clickzetta.com` |
106
+
107
+ After creating, verify with:
108
+ ```bash
109
+ cz-cli status --profile <name>
110
+ ```
111
+
112
+ Note: `profile discover` / `list-workspaces` / `render-command` require a Studio page URL (`https://<instance>.accounts.clickzetta.com`) and are not usable when only a service endpoint + credentials are available.
113
+
82
114
  ## Step-by-step: no credential available
83
115
 
84
116
  If the user doesn't have a base64 credential, guide them to:
@@ -34,7 +34,8 @@ cz-cli task list List Studio tasks
34
34
  cz-cli task create <name> --type <TYPE> Create task (SQL/PYTHON/SHELL/SPARK/FLOW)
35
35
  cz-cli task content <task> Get task script and config
36
36
  cz-cli task save-content <task> --file <f> Save task script
37
- cz-cli task save-config <task> Save task schedule config
37
+ cz-cli task save-config <task> Save task non-cron config, like retry, dependency
38
+ cz-cli task save-cron <task> Save task schedule config
38
39
  cz-cli task deps <task> Show task dependencies (draft)
39
40
  cz-cli task online <task> Publish a task
40
41
  cz-cli task offline <task> Take task offline (irreversible)
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: dt-creator
3
+ description: |
4
+ 创建 Dynamic Table 的参考资料索引。涵盖静态分区 DT 与动态分区 DT 的声明策略、
5
+ 增量计算支持的 SQL 模式、增量刷新配置项说明、以及刷新历史的查询方式。
6
+ ---
7
+
8
+ # DT Creator — 参考资料索引
9
+
10
+ ## references/
11
+
12
+ - **dt-declaration-strategy.md** — DT 声明策略(静态分区 DT vs 动态分区 DT 的创建语法与选择)
13
+ - **sql-limitations.md** — SQL 支持矩阵(JOIN、聚合、窗口函数、非确定性函数等的支持情况)
14
+ - **incremental-config-reference.md** — 增量计算配置参考(刷新策略、源表特征声明、状态表管理等)
15
+ - **refresh-history-guide.md** — 增量刷新历史查询(SHOW REFRESH HISTORY / DESC HISTORY / information_schema)
@@ -0,0 +1,185 @@
1
+ # Dynamic Table 声明策略
2
+
3
+ DT 有两种创建语法:静态分区 DT 和动态分区 DT(非分区 DT 可视为动态分区的特例)。两者在创建语法、刷新方式、增量行为上有本质区别。
4
+
5
+ ## 核心概念
6
+
7
+ ### 静态分区 DT(Partitioned DT with SESSION_CONFIGS args)
8
+
9
+ SQL 中通过 `SESSION_CONFIGS()` 引用分区参数,每次 REFRESH 时指定具体的分区值。每个分区独立刷新,可以视为每个分区刷新单元都是一个彼此独立的 DT。
10
+
11
+ ```sql
12
+ CREATE DYNAMIC TABLE order_daily (
13
+ id BIGINT, amount DECIMAL(12,2), ds STRING
14
+ )
15
+ PARTITIONED BY (ds)
16
+ AS
17
+ SELECT id, amount, SESSION_CONFIGS()['dt.args.ds'] AS ds
18
+ FROM orders
19
+ WHERE ds = SESSION_CONFIGS()['dt.args.ds'];
20
+
21
+ -- 刷新时指定分区(在 Studio 任务中执行,dt.args.ds 通过任务参数传入)
22
+ REFRESH DYNAMIC TABLE order_daily PARTITION(ds = '2025-01-01');
23
+ ```
24
+
25
+ ### 动态分区 DT(Non-partitioned DT / DT without args)
26
+
27
+ SQL 中不引用 `SESSION_CONFIGS()`,或者虽然有分区但分区值由查询逻辑动态产生。每次 REFRESH 处理所有源表的增量数据。
28
+
29
+ 动态分区 DT 不允许除 REFRESH 以外的任何命令修改数据(INSERT/UPDATE/DELETE/MERGE 均不可用),数据完全由 REFRESH 驱动。
30
+
31
+ 因此以下 ETL 场景不适合使用动态分区 DT:
32
+ - 需要手动修补数据(如发现某几行数据有误,需要直接 UPDATE 修正)
33
+ - 需要按条件删除部分数据(如清理脏数据、删除过期记录)
34
+ - 需要 MERGE INTO 做 upsert(如 CDC 场景中消费 stream 合并到目标表)
35
+ - 需要 INSERT INTO 追加外部数据(如手动导入一批补录数据)
36
+ - 需要按分区独立回填或重刷(动态分区 DT 只能整表全量刷新,无法单独刷某个分区)
37
+ - 下游有其他任务需要往同一张表写入数据(DT 独占写入权)
38
+
39
+ ```sql
40
+ CREATE DYNAMIC TABLE order_summary (
41
+ category STRING, total_amount DECIMAL(12,2)
42
+ )
43
+ AS
44
+ SELECT category, SUM(amount) AS total_amount
45
+ FROM orders
46
+ GROUP BY category;
47
+
48
+ -- 刷新时不指定分区
49
+ REFRESH DYNAMIC TABLE order_summary;
50
+ ```
51
+
52
+ ## 两者的关键区别
53
+
54
+ | 维度 | 静态分区 DT | 动态分区 DT |
55
+ |------|-----------|-----------|
56
+ | SQL 中是否有 `SESSION_CONFIGS()` | 有,用于引用分区参数 | 无 |
57
+ | REFRESH 语法 | `REFRESH ... PARTITION(ds='xxx')` | `REFRESH ...`(无 PARTITION) |
58
+ | 增量范围 | 只处理指定分区的增量数据 | 处理所有源表的全部增量数据 |
59
+ | 调度方式 | 外部调度器按分区值逐个触发 | 外部调度器定时触发即可 |
60
+ | 数据生命周期 | 按分区管理,可独立回填/删除 | 整表管理 |
61
+ | 状态表 | 按分区独立维护 | 全局维护 |
62
+ | 适合的数据模式 | T+1 批处理、按时间分区的 ETL | 实时流、全局聚合、无明确分区键 |
63
+
64
+ ## 选择决策树
65
+
66
+ ```
67
+ 你的数据有明确的时间/业务分区键吗?
68
+
69
+ ├─ 是 → 原始 ETL 是按分区 INSERT OVERWRITE 的吗?
70
+ │ │
71
+ │ ├─ 是 → 使用静态分区 DT
72
+ │ │ (保持原有的分区粒度,每个分区独立刷新)
73
+ │ │
74
+ │ └─ 否 → 数据量大吗?需要按分区管理生命周期吗?
75
+ │ │
76
+ │ ├─ 是 → 使用静态分区 DT
77
+ │ │ (即使原来不是分区表,也建议加分区以便管理)
78
+ │ │
79
+ │ └─ 否 → 使用动态分区 DT
80
+ │ (简单场景,不需要分区管理)
81
+
82
+ └─ 否 → 使用动态分区 DT
83
+ (全局聚合、实时汇总等场景)
84
+ ```
85
+
86
+ ## 静态分区 DT 详解
87
+
88
+ ### 适用场景
89
+
90
+ 1. **T+1 批处理 ETL 迁移**
91
+ - 原始 SQL 是 `INSERT OVERWRITE TABLE t PARTITION(ds='${ds}')` 模式
92
+ - 每天/每小时按分区刷新一次
93
+ - 需要支持历史分区回填
94
+
95
+ 2. **滑动窗口计算**
96
+ - 如:最近 7 天的聚合、环比计算
97
+ - SQL 中引用 `SESSION_CONFIGS()['dt.args.ds']` 和 `sub_days(...)` 做窗口范围
98
+
99
+ 3. **需要按分区管理数据生命周期**
100
+ - 通过 `data_lifecycle` 自动清理过期分区
101
+ - 可以单独回填某个分区而不影响其他分区
102
+
103
+ 4. **自引用 DT(日环比、SCD)**
104
+ - 当前分区依赖上一个分区的结果
105
+ - 必须用静态分区,因为需要明确指定"当前分区"和"上一分区"
106
+
107
+ ### 刷新方式
108
+
109
+ > ⚠️ **重要**:静态分区 DT 的 `dt.args.*` 参数**仅在 Studio 任务中可用**,不能在交互式 SQL 中使用 `SET dt.args.xxx`。
110
+ > 必须通过 Studio 创建调度任务,在任务参数中配置 `dt.args.ds` 等值。
111
+
112
+ ```sql
113
+ -- 在 Studio 任务中,参数通过任务配置传入,REFRESH 语句指定分区值:
114
+ REFRESH DYNAMIC TABLE my_dt PARTITION(ds = '2025-01-15');
115
+
116
+ -- 多级分区
117
+ REFRESH DYNAMIC TABLE my_dt PARTITION(pt = '20250411', pt_hour = '01');
118
+ ```
119
+
120
+ ### 注意事项
121
+
122
+ - 回填时使用 `SET cz.optimizer.incremental.backfill.enabled = TRUE`(此配置在交互式 SQL 中可用)
123
+ - `dt.args.*` 参数仅在 Studio 任务中可用,不能在交互式 SQL 中 SET
124
+ - `cz.optimizer.*` 配置项在交互式 SQL 中可用
125
+
126
+ ## 动态分区 DT 详解
127
+
128
+ ### 适用场景
129
+
130
+ 1. **实时流数据聚合**
131
+ - 源表持续写入,DT 定时刷新
132
+ - 不需要按分区管理,每次处理所有新增数据
133
+
134
+ 2. **全局汇总表**
135
+ - 如:全局 TopN、全局计数、全局去重
136
+ - 没有明确的分区键
137
+
138
+ 3. **简单的 JOIN + 过滤**
139
+ - 不涉及分区参数的简单转换
140
+ - 如:事实表 JOIN 维度表,输出宽表
141
+
142
+ 4. **多源表合并(UNION ALL)**
143
+ - 多个源表的数据合并到一张表
144
+ - 不需要按分区管理
145
+
146
+ ### 刷新方式
147
+
148
+ ```sql
149
+ -- 直接刷新,处理所有源表的增量
150
+ REFRESH DYNAMIC TABLE my_dt;
151
+ ```
152
+
153
+ ### 注意事项
154
+
155
+ - 每次刷新处理所有源表的全部增量,如果源表变更量大,刷新可能较慢
156
+ - 状态表全局维护,随着数据量增长可能膨胀
157
+ - 不支持按分区回填,只能全量刷新整表
158
+ - 适合变更量占比小的场景(< 5%)
159
+
160
+ ## 分区粒度选择
161
+
162
+ 当选择静态分区 DT 时,还需要决定分区粒度:
163
+
164
+ | 数据模式 | 推荐分区粒度 | 说明 |
165
+ |---------|------------|------|
166
+ | 严格有序的时间序列(如日志) | 分钟级 (`dt_min`) | 数据量大、写入频繁 |
167
+ | 大致有序、少量迟到数据 | 小时级 (`dt_hour`) | 平衡粒度和管理复杂度 |
168
+ | T+1 批量导入 | 天级 (`ds`) | 最常见的 ETL 场景 |
169
+ | 按业务周期 | 周/月级 | 报表类场景 |
170
+ | 多级分区 | 天 + 小时 (`ds`, `hour`) | 需要更细粒度的生命周期管理 |
171
+
172
+ 选择原则:
173
+ - 粒度越细,每次刷新处理的数据量越小,增量效率越高
174
+ - 粒度越细,分区数越多,管理和调度越复杂
175
+ - 粒度应与数据写入频率匹配:如果数据每小时写入一次,分区粒度不应细于小时
176
+
177
+ ## 从原始 ETL 判断分区策略
178
+
179
+ | 原始 ETL 模式 | 推荐 DT 分区策略 |
180
+ |--------------|----------------|
181
+ | `INSERT OVERWRITE TABLE t PARTITION(ds='${ds}')` | 静态分区 DT,天级 |
182
+ | `INSERT OVERWRITE TABLE t PARTITION(ds='${ds}', hour='${hour}')` | 静态分区 DT,天+小时级 |
183
+ | `INSERT OVERWRITE TABLE t PARTITION(ds)` (动态分区写入) | 动态分区 DT 或静态分区 DT(取决于是否需要按分区管理) |
184
+ | `INSERT INTO TABLE t SELECT ...` (无分区) | 动态分区 DT |
185
+ | `INSERT OVERWRITE TABLE t SELECT ...` (全表覆盖) | 动态分区 DT |
@@ -0,0 +1,429 @@
1
+ # Dynamic Table 增量计算配置参考
2
+
3
+ 本文档列出 Dynamic Table / Materialized View 增量刷新中可供用户调整的配置项。所有配置均通过 `SET` 语句在 Session 级别生效。
4
+
5
+ ---
6
+
7
+ ## 刷新策略
8
+
9
+ 控制增量刷新与全量刷新之间的切换行为。
10
+
11
+ ### `cz.optimizer.incremental.force.full.refresh`
12
+
13
+ - 类型:bool,默认值:`false`
14
+
15
+ 强制当次刷新使用全量模式,跳过增量逻辑,对所有源表做全量扫描重算。
16
+
17
+ **适用场景:**
18
+ - 增量刷新结果出现数据异常(如数据缺失、重复),需要做一次全量修复
19
+ - 维度表发生了重要变更(如修正了映射关系),需要让所有历史数据重新 JOIN 到最新维度
20
+ - DT 的状态表被误删或损坏,增量刷新报错,需要从头重算
21
+
22
+ **优势:** 全量重算可以保证结果与直接执行 SQL 完全一致,是最可靠的数据修复手段。
23
+
24
+ **风险:** 全量刷新需要扫描所有源表的全部数据,计算量和耗时远大于增量刷新。对于大数据量的 DT,一次全量刷新可能需要数分钟甚至数小时。
25
+
26
+ **注意:** 这是一个 Session 级别的一次性开关。刷新完成后必须手动设回 `false`,否则后续每次 REFRESH 都会走全量,浪费计算资源。
27
+
28
+ ```sql
29
+ SET cz.optimizer.incremental.force.full.refresh = true;
30
+ REFRESH DYNAMIC TABLE my_dt;
31
+ SET cz.optimizer.incremental.force.full.refresh = false;
32
+ ```
33
+
34
+ ### `cz.optimizer.incremental.try.incremental.refresh.enabled`
35
+
36
+ - 类型:bool,默认值:`false`
37
+
38
+ 优先尝试增量刷新;若增量计划生成失败(如 SQL 中包含不支持增量的算子),自动回退至全量刷新,而非直接报错。
39
+
40
+ **适用场景:**
41
+ - 刚将一个复杂 SQL 迁移为 DT,不确定所有算子是否都支持增量计算,希望"能增量就增量,不行就全量"
42
+ - 生产环境中希望保证刷新任务不因增量计划生成失败而中断
43
+
44
+ **优势:** 提高了 DT 刷新的容错性。即使 SQL 中包含增量引擎暂不支持的模式,刷新任务也不会失败,而是自动降级为全量刷新。
45
+
46
+ **风险:** 如果增量计划生成持续失败,每次刷新都会静默回退为全量,用户可能不知道自己的 DT 一直在走全量,浪费计算资源。建议配合日志监控,关注是否频繁回退。
47
+
48
+ ```sql
49
+ -- 在 REFRESH 语句前执行
50
+ SET cz.optimizer.incremental.try.incremental.refresh.enabled = true;
51
+ REFRESH DYNAMIC TABLE my_dt;
52
+ ```
53
+
54
+ ---
55
+
56
+ ## 源表数据特征声明
57
+
58
+ 通过声明源表的数据特征,引导增量引擎选择更高效的计算策略。
59
+
60
+ ### `cz.optimizer.incremental.dimension.tables`
61
+
62
+ - 类型:string,默认值:`""`
63
+
64
+ 将指定的源表标记为维度表。标记后,增量引擎不再读取该表的变更数据,每次刷新时直接读取其最新全量数据。只有非维度表(事实表)的变更才会驱动增量计算。
65
+
66
+ 格式为逗号或冒号分隔的表名,支持完整路径 `instanceId.ws.schema.table` 或简短名称。
67
+
68
+ **这是一个用正确性换性能的权衡。** 标记为维度表后,该表的任何数据变更(INSERT/UPDATE/DELETE)都不会触发增量计算,已输出的结果行不会因维度表变更而更新。作为回报,增量引擎可以获得显著的性能提升:
69
+ - 跳过维度表的变更数据扫描(不需要读取变更日志)
70
+ - 减少状态表数量(JOIN 一侧全是维度表时不需要创建状态表)
71
+ - 简化增量计划(只需要用事实表的变更数据 JOIN 维度表的全量数据,不需要反向计算)
72
+ - 减少增量数据的去重合并操作
73
+
74
+ **适用场景:**
75
+ - 事实表 LEFT JOIN 码表/字典表(如地区码表、产品分类表),码表极少变更,不需要跟踪其变更
76
+ - 大事实表 JOIN 小维度表,核心诉求是事实表的增量性能,维度表偶尔变更后可以接受短暂不一致
77
+ - 外部表(如 MySQL 外表)不支持 time travel,无法提供变更数据,标记为维度表后可正常增量计算
78
+ - T+1 维度表 + 实时事实表:维度表每天批量更新一次,在两次更新之间可视为不变
79
+
80
+ **正确性影响:** 维度表变更后,已输出的结果不会自动更新。例如维度表中某行的 `name` 从 `'A'` 改为 `'B'`,已经 JOIN 过该行的历史结果仍然显示 `'A'`。只有新的事实表增量才会 JOIN 到最新的 `'B'`。如果需要订正历史数据,必须手动执行一次全量刷新。
81
+
82
+ 详细的正确性影响分析和各 JOIN 类型下的行为,请参阅《维度表 JOIN 场景详解》文档(dimension-table-join-guide)。
83
+
84
+ ```sql
85
+ -- 推荐:通过 DT 表属性声明(跟随 DT 定义,不需要每次 REFRESH 前设置)
86
+ CREATE DYNAMIC TABLE my_dt
87
+ TBLPROPERTIES('mv_const_tables' = 'dim_product,dim_region')
88
+ AS SELECT ...;
89
+
90
+ -- 查看已设置的 TBLPROPERTIES(⚠️ 不支持 SHOW TBLPROPERTIES 语法)
91
+ SHOW CREATE TABLE my_dt;
92
+
93
+ -- 或通过 Session 配置(在 REFRESH 语句前执行)
94
+ SET cz.optimizer.incremental.dimension.tables = 'dim_product,dim_region';
95
+ REFRESH DYNAMIC TABLE my_dt;
96
+
97
+ -- 维度表发生重要变更后,手动全量刷新订正数据
98
+ SET cz.optimizer.incremental.force.full.refresh = true;
99
+ REFRESH DYNAMIC TABLE my_dt;
100
+ SET cz.optimizer.incremental.force.full.refresh = false;
101
+ ```
102
+
103
+ ### `cz.optimizer.incremental.append.only.tables`
104
+
105
+ - 类型:string,默认值:`""`
106
+
107
+ 将指定的源表标记为"预期仅追加"。这是一个优化 hint,告诉优化器该表预期只有 INSERT 操作,优化器据此选择更高效的增量计划(如提前创建针对仅追加场景优化的中间状态)。
108
+
109
+ **这不影响正确性。** 即使标记为 append-only 的表后续实际发生了 UPDATE 或 DELETE,增量引擎仍然会正常捕获并计算这些变更,结果不会出错。区别在于:当实际发生了 UPDATE/DELETE 时,优化器之前基于"仅追加"假设选择的计划可能不是最优的,性能上可能不如未标记时的计划。
110
+
111
+ **适用场景:**
112
+ - Kafka 消费落地表、日志表、埋点表等绝大多数时候只有 INSERT 的数据源
113
+ - 源表偶尔可能有少量 UPDATE/DELETE(如数据修正),但主要写入模式是 INSERT
114
+
115
+ **优势:** 优化器基于"仅追加"假设可以选择更高效的增量计划,减少不必要的中间状态维护开销。对于聚合场景,可以直接累加而不需要维护完整的中间状态。性能提升显著,尤其是在包含 JOIN 和聚合的复杂 SQL 中。
116
+
117
+ **风险:** 如果该表实际频繁发生 UPDATE/DELETE,优化器基于"仅追加"假设选择的计划可能不是最优的,增量刷新性能可能不如不标记时好。但结果的正确性不受影响。
118
+
119
+ ```sql
120
+ -- 推荐:通过表属性声明(永久生效,不需要每次刷新前设置)
121
+ ALTER TABLE event_log SET PROPERTIES('INCR_APPEND_ONLY_TABLE' = 'true');
122
+
123
+ -- 或通过 Session 配置(在 REFRESH 语句前执行)
124
+ SET cz.optimizer.incremental.append.only.tables = 'event_log,click_stream';
125
+ REFRESH DYNAMIC TABLE my_dt;
126
+ ```
127
+
128
+ ---
129
+
130
+ ## 全量刷新回退策略
131
+
132
+ 当源表变更量过大或特定表发生变更时,自动从增量切换为全量刷新。
133
+
134
+ ### `cz.optimizer.incremental.full.refresh.if.these.tables.change`
135
+
136
+ - 类型:string,默认值:`""`
137
+
138
+ 逗号分隔的表名列表。当列表中的任意表在本次刷新周期内有数据变更时,自动触发全量刷新。
139
+
140
+ **适用场景:**
141
+ - DT 的 SQL 中 JOIN 了一张关键维度表(如价格表、汇率表),该表一旦变更,所有历史数据都需要按新值重算
142
+ - 与 `cz.optimizer.incremental.dimension.tables` 的区别:`dimension.tables` 是忽略变更继续增量;本配置是检测到变更后触发全量重算
143
+
144
+ **优势:** 保证了关键表变更后结果的正确性——一旦检测到变更,自动全量重算,不需要人工干预。
145
+
146
+ **风险:** 如果指定的表变更频繁(如每小时都有更新),每次刷新都会触发全量,完全失去增量的性能优势。应仅用于变更频率极低但变更影响面大的表。
147
+
148
+ ```sql
149
+ -- 在 REFRESH 语句前执行
150
+ SET cz.optimizer.incremental.full.refresh.if.these.tables.change = 'dim_pricing,dim_exchange_rate';
151
+ REFRESH DYNAMIC TABLE my_dt;
152
+ ```
153
+
154
+ ### `cz.optimizer.incremental.full.refresh.if.source.table.changes.significantly`
155
+
156
+ - 类型:bool,默认值:`false`
157
+
158
+ 启用后,当源表的增量数据量占全量数据量的比例超过阈值时,自动切换为全量刷新。
159
+
160
+ **适用场景:**
161
+ - 源表偶尔会发生大批量数据导入(如历史数据回灌),此时增量数据量接近甚至超过全量,增量刷新反而比全量更慢
162
+ - 希望系统自动判断"增量划不划算",不划算时自动切全量
163
+
164
+ **优势:** 自动在增量和全量之间选择最优策略,避免增量数据量过大时增量刷新反而更慢的问题(增量需要额外的变更数据计算、去重合并、状态表读写等开销)。
165
+
166
+ **风险:** 阈值判断基于统计信息,可能不完全准确。如果统计信息不精确,可能出现不必要的全量刷新或该切全量时没切的情况。
167
+
168
+ 需配合 `cz.optimizer.incremental.threshold.of.source.table.change.for.full.refresh` 设置阈值。
169
+
170
+ ### `cz.optimizer.incremental.threshold.of.source.table.change.for.full.refresh`
171
+
172
+ - 类型:double,默认值:`1.0`
173
+
174
+ 触发全量刷新的变更比例阈值。当增量数据量 / 全量数据量超过此值时触发全量刷新。
175
+
176
+ - `1.0`:增量数据量超过全量时才触发(非常保守)
177
+ - `0.5`:增量超过全量的一半就触发
178
+ - `0.1`:增量超过全量的 10% 就触发(激进,适用于增量计算开销较大的复杂 SQL)
179
+
180
+ ```sql
181
+ -- 在 REFRESH 语句前执行
182
+ SET cz.optimizer.incremental.full.refresh.if.source.table.changes.significantly = true;
183
+ SET cz.optimizer.incremental.threshold.of.source.table.change.for.full.refresh = 0.5;
184
+ REFRESH DYNAMIC TABLE my_dt;
185
+ ```
186
+
187
+ ---
188
+
189
+ ## 状态表管理
190
+
191
+ 状态表是增量引擎在刷新过程中自动创建的内部表,用于存储中间计算结果(如聚合的中间状态、JOIN 的历史数据等),以加速后续的增量刷新。
192
+
193
+ ### `cz.optimizer.incremental.enable.state.table`
194
+
195
+ - 类型:bool,默认值:`true`
196
+
197
+ 状态表总开关。系统默认限制每个 DT 最多创建 5 个状态表,以防止极端场景下状态表过多导致磁盘存储占用过大。当 DT 的 SQL 中包含的有状态计算算子(如聚合、JOIN、窗口函数等)超过 5 个时,若用户未显式开启此配置,系统将**放弃创建所有状态表**,增量刷新退化为每次从源表重新计算中间结果。
198
+
199
+ 如果用户希望为这些算子创建状态表以获得更优的增量刷新性能,需要显式设置此配置为 `true`。**显式开启此配置意味着用户理解并接受以额外的磁盘存储为代价换取更优的增量刷新性能。**
200
+
201
+ 设为 `false` 后,增量引擎不创建也不复用任何状态表,所有中间结果每次都从源表重新计算。
202
+
203
+ **适用场景:**
204
+
205
+ 设为 `true`(显式开启):
206
+ - DT 的 SQL 包含大量有状态算子(如多层 JOIN + 聚合 + 窗口函数),默认的 5 个状态表限制不足以覆盖所有算子,希望创建更多状态表以获得最优增量性能
207
+ - 用户已评估存储开销,确认可以接受额外的状态表存储占用
208
+
209
+ 设为 `false`(关闭):
210
+ - 排查状态表相关的问题(如怀疑状态表数据不一致导致增量结果异常)
211
+ - 源表数据量较小,全量重算的代价可以接受,不需要状态表加速
212
+ - 需要严格控制存储开销,不希望系统自动创建额外的表
213
+
214
+ **优势:** 显式开启后,系统可以为所有有状态算子创建状态表,最大化增量刷新的性能收益。关闭后则消除了状态表带来的所有存储开销。
215
+
216
+ **风险:** 显式开启后,状态表数量可能超过默认的 5 个限制,带来额外的磁盘存储占用。关闭后,包含聚合或多表 JOIN 的复杂 DT 每次增量刷新都需要读取源表的全量数据来重算中间结果,性能可能显著下降。
217
+
218
+ ```sql
219
+ -- 显式开启:允许系统为所有有状态算子创建状态表(在 REFRESH 语句前执行)
220
+ SET cz.optimizer.incremental.enable.state.table = true;
221
+ REFRESH DYNAMIC TABLE my_dt;
222
+
223
+ -- 关闭:不创建也不复用任何状态表
224
+ SET cz.optimizer.incremental.enable.state.table = false;
225
+ REFRESH DYNAMIC TABLE my_dt;
226
+ ```
227
+
228
+ ### `cz.optimizer.incremental.state.table.lifecycle`
229
+
230
+ - 类型:string,默认值:`"3"`
231
+
232
+ 状态表数据保留天数。超过此天数的历史版本数据将被自动清理。
233
+
234
+ **适用场景:**
235
+ - DT 的刷新间隔较长(如每周一次),默认 3 天会导致状态表在两次刷新之间被清理,下次刷新时无法复用状态表,退化为全量刷新。此时需要增大此值
236
+ - 希望减少状态表的存储占用,可以适当缩短保留期(但不能短于刷新间隔)
237
+ - 状态表内容很大,希望及时回收存储空间,可以显式缩短生命周期(例如设为 1 天),让过期版本尽快被清理
238
+
239
+ **优势:** 增大保留期可以确保状态表在刷新间隔内不被清理,保证增量刷新能正常复用状态。
240
+
241
+ **风险:** 保留期越长,状态表占用的存储空间越大。每个版本的状态表都会保留到过期,如果刷新频率高(如每小时一次)且保留期长(如 30 天),状态表的存储量会非常可观。
242
+
243
+ ```sql
244
+ -- 在 REFRESH 语句前执行
245
+ SET cz.optimizer.incremental.state.table.lifecycle = '10';
246
+ REFRESH DYNAMIC TABLE my_dt;
247
+ ```
248
+
249
+ ### `cz.optimizer.incremental.rebuild.rule.based.state.table`
250
+
251
+ - 类型:bool,默认值:`false`
252
+
253
+ 设为 `true` 后,下次刷新时重建所有状态表。重建过程会清除旧的状态表数据,基于当前源表数据重新生成。
254
+
255
+ **适用场景:**
256
+ - 状态表数据损坏(如因系统异常导致状态表写入不完整),增量刷新结果异常
257
+ - DT 的 SQL 发生了变更(如修改了聚合逻辑),旧的状态表 Schema 与新 SQL 不匹配
258
+ - 增量刷新持续报错,怀疑是状态表问题,希望从头重建
259
+
260
+ **优势:** 重建后状态表数据与当前源表完全一致,消除了历史累积的数据不一致问题。
261
+
262
+ **风险:** 重建过程中该次刷新会走全量,耗时较长。重建完成前,增量刷新不可用。
263
+
264
+ **注意:** 这是一个一次性开关。重建完成后必须设回 `false`,否则每次刷新都会重建状态表,完全失去增量的意义。
265
+
266
+ ```sql
267
+ SET cz.optimizer.incremental.rebuild.rule.based.state.table = true;
268
+ REFRESH DYNAMIC TABLE my_dt;
269
+ SET cz.optimizer.incremental.rebuild.rule.based.state.table = false;
270
+ ```
271
+
272
+ ### `cz.optimizer.incremental.state.table.specified.schema`
273
+
274
+ - 类型:string,默认值:`""`
275
+
276
+ 指定状态表存放的 Schema。默认情况下,状态表与 DT 目标表在同一个 Schema 中。
277
+
278
+ **适用场景:**
279
+ - 希望将状态表与业务表隔离,便于统一管理和监控状态表的存储占用
280
+ - 多个 DT 共享同一个 Schema 存放状态表,方便批量清理
281
+
282
+ **优势:** 业务表和状态表分离后,可以独立设置 Schema 级别的权限、配额和生命周期策略,避免状态表干扰业务表的管理。
283
+
284
+ **风险:** 跨 Schema 访问可能带来轻微的元数据查询开销。此外,如果指定的 Schema 不存在或权限不足,状态表创建会失败。
285
+
286
+ ```sql
287
+ SET cz.optimizer.incremental.state.table.specified.schema = 'incr_state';
288
+ ```
289
+
290
+ ---
291
+
292
+ ## DT 定义变更
293
+
294
+ 控制 `CREATE OR REPLACE DYNAMIC TABLE` 时的兼容性检查行为。
295
+
296
+ ### `cz.sql.mv.check.before.replacing.sql`
297
+
298
+ - 类型:bool,默认值:`true`
299
+
300
+ 控制 `CREATE OR REPLACE DYNAMIC TABLE` 时是否对新旧 SQL 进行兼容性检查。
301
+
302
+ **开启检查(`true`,默认):** 系统会比较新旧 SQL 的列结构,判断是否兼容。如果判定为兼容(如仅新增列),系统会保留已有的增量状态,后续继续增量刷新。但兼容性判断并非完美——对于被判定为"兼容"的变更,新增列在历史数据中将填充 NULL,且已有的历史行不会按新 SQL 重新计算,可能导致新旧数据不一致。
303
+
304
+ **关闭检查(`false`):** 系统跳过兼容性检查,直接认定新旧 SQL 不兼容,重置增量状态(清除状态表和历史版本信息)。替换后的下一次刷新将执行全量计算,确保所有数据按新 SQL 重新生成。
305
+
306
+ **适用场景:**
307
+
308
+ 设为 `false`(关闭检查):
309
+ 1. **`CREATE OR REPLACE` 卡住或报错**:某些情况下兼容性检查本身可能耗时较长或因元数据问题报错,导致 `CREATE OR REPLACE` 无法完成。关闭检查可跳过该步骤,让替换操作顺利完成。代价是下次刷新会变成全量。
310
+ 2. **SQL 发生了实质性变更,希望从头重算**:如修改了 JOIN 逻辑、聚合方式等核心计算逻辑,需要全量重算以保证数据正确性。关闭检查可确保系统不会错误地判定为"兼容"而保留旧的增量状态。
311
+
312
+ 保持 `true`(开启检查,默认):
313
+ 1. **仅新增列等简单变更**:希望系统自动判断兼容性,兼容时保留增量状态避免全量刷新。适用于对历史数据中新增列为 NULL 可以接受的场景。
314
+ 2. **日常迭代中频繁调整 DT 定义**:依赖系统自动判断,减少不必要的全量刷新。
315
+
316
+ **开启检查的风险:** 兼容性判断可能将实际不完全兼容的变更判定为"兼容",导致新增列在历史数据中为 NULL,或已有历史行永远不会按新 SQL 更新。
317
+
318
+ **关闭检查的风险:** 下一次刷新将执行全量计算,对于大数据量的 DT 可能耗时较长。
319
+
320
+ ```sql
321
+ -- 关闭检查,确保替换后全量重算
322
+ SET cz.sql.mv.check.before.replacing.sql = false;
323
+ CREATE OR REPLACE DYNAMIC TABLE my_dt AS SELECT ...;
324
+ SET cz.sql.mv.check.before.replacing.sql = true;
325
+ -- 注意:下次 REFRESH 将执行全量刷新
326
+ ```
327
+
328
+ ---
329
+
330
+ ## 历史分区回填(Backfill)
331
+
332
+ ### `cz.optimizer.incremental.backfill.enabled`
333
+
334
+ - 类型:bool,默认值:`false`
335
+
336
+ 启用回填模式。用于对 DT 的历史分区进行数据回填或修正。开启后,系统会自动执行以下操作:
337
+ - 强制当次刷新使用全量模式(等同于开启 `force.full.refresh`)
338
+ - 跳过增量数据的读取,避免读取大量历史变更日志
339
+ - 对于分区 DT,禁用状态表的创建和匹配(因为回填的分区不需要增量状态)
340
+ - 允许对 DT 执行 DML 操作(如 `INSERT OVERWRITE`)
341
+
342
+ **适用场景:**
343
+
344
+ 设为 `true`(开启回填):
345
+ 1. **历史分区数据修正**:某个历史分区的数据出现问题,需要用正确的源数据重新生成该分区。
346
+ 2. **新建 DT 后补充历史数据**:DT 创建后,需要为已有的历史分区逐个生成数据。
347
+ 3. **源表数据回灌后重算**:源表进行了历史数据回灌,需要对受影响的分区重新计算。
348
+
349
+ **注意:**
350
+ - 回填模式是一次性操作,回填完成后应设回 `false`,否则后续每次刷新都会走全量。
351
+ - 回填模式下不会创建或更新状态表,因此不会影响后续正常增量刷新的状态。
352
+ - 回填通常配合 `INSERT OVERWRITE` 使用,覆盖目标分区的已有数据。
353
+
354
+ ```sql
355
+ -- 在 Studio 任务中,参数通过任务配置传入:
356
+ SET cz.optimizer.incremental.backfill.enabled = true;
357
+ REFRESH DYNAMIC TABLE my_dt PARTITION(ds = '2025-01-01');
358
+ SET cz.optimizer.incremental.backfill.enabled = false;
359
+
360
+ -- 也可以通过 INSERT OVERWRITE 直接回填
361
+ SET cz.optimizer.incremental.backfill.enabled = true;
362
+ INSERT OVERWRITE TABLE my_dt
363
+ SELECT id, amount, '2025-01-01' AS ds
364
+ FROM source_table
365
+ WHERE ds = '2025-01-01';
366
+ SET cz.optimizer.incremental.backfill.enabled = false;
367
+ ```
368
+
369
+ ---
370
+
371
+ ## 全量刷新时分区表的写入行为
372
+
373
+ ### `cz.optimizer.incremental.full.refresh.overwrite.partitioned.table`
374
+
375
+ - 类型:bool,默认值:`true`
376
+
377
+ 控制分区 DT 在全量刷新时的写入模式。
378
+
379
+ **背景:** 对于分区表,全量刷新(`force.full.refresh = true` 或系统自动触发的全量刷新)默认采用覆盖写入(OVERWRITE)模式——这是大数据领域的通用行为,即全量重算的结果会覆盖目标表的所有分区。但在某些场景下,DT 的 SQL 只计算部分分区的数据(例如只查询最近 7 天),全量刷新的结果也只包含这部分分区。此时如果使用覆盖写入,会导致历史分区(如 7 天前的数据)被清空。
380
+
381
+ **开启覆盖(`true`,默认):** 全量刷新时,目标表的所有分区都会被覆盖。刷新结果中不包含的分区将被清空。这适用于 DT 的 SQL 覆盖了目标表的全部数据范围的场景。
382
+
383
+ **关闭覆盖(`false`):** 全量刷新时,只写入本次计算产生的分区数据,不影响目标表中已有的其他分区。历史分区的数据保持不变。
384
+
385
+ **适用场景:**
386
+
387
+ 设为 `false`(关闭覆盖):
388
+ 1. **DT 的 SQL 只计算部分分区**:例如 SQL 中有 `WHERE ds >= '2025-01-01'` 的过滤条件,只计算最近一段时间的数据。全量刷新时不希望清空更早的历史分区。
389
+ 2. **按分区逐步积累数据的 DT**:每次刷新只产生当前分区的数据,历史分区由之前的刷新产生。全量刷新时只需要重算当前分区,不应影响历史分区。
390
+ 3. **滑动窗口场景**:DT 的 SQL 基于分区参数计算一个时间窗口内的数据,全量刷新时只重算窗口内的分区。
391
+
392
+ 保持 `true`(开启覆盖,默认):
393
+ 1. **DT 的 SQL 覆盖全部数据**:SQL 没有分区过滤条件,全量刷新的结果包含目标表的所有数据。
394
+ 2. **需要全量重建目标表**:希望全量刷新后目标表的数据与直接执行 SQL 的结果完全一致,不保留任何历史残留。
395
+
396
+ **风险:**
397
+ - 开启覆盖时,如果 DT 的 SQL 只计算部分分区,全量刷新会清空未被计算到的历史分区,导致数据丢失。
398
+ - 关闭覆盖时,如果 DT 的 SQL 覆盖全部数据,全量刷新后目标表中可能残留旧数据(因为旧分区没有被清空),导致数据不一致。
399
+
400
+ ```sql
401
+ -- 关闭覆盖:全量刷新时保留历史分区(在 REFRESH 语句前执行)
402
+ SET cz.optimizer.incremental.full.refresh.overwrite.partitioned.table = false;
403
+ SET cz.optimizer.incremental.force.full.refresh = true;
404
+ REFRESH DYNAMIC TABLE my_dt;
405
+ SET cz.optimizer.incremental.force.full.refresh = false;
406
+ ```
407
+
408
+ ---
409
+
410
+ ## 配置速查表
411
+
412
+ 按使用场景快速定位所需配置:
413
+
414
+ | 场景 | 配置项 | 推荐值 |
415
+ |------|--------|--------|
416
+ | 数据异常,需要全量重算修复 | `cz.optimizer.incremental.force.full.refresh` | `true`(一次性) |
417
+ | 不确定 SQL 是否支持增量 | `cz.optimizer.incremental.try.incremental.refresh.enabled` | `true` |
418
+ | 小表 JOIN 不需要跟踪变更 | `cz.optimizer.incremental.dimension.tables` 或表属性 `mv_const_tables` | 表名列表 |
419
+ | 源表主要是 INSERT,希望优化增量性能 | `cz.optimizer.incremental.append.only.tables` 或表属性 `INCR_APPEND_ONLY_TABLE` | 表名列表 / `true` |
420
+ | 关键表变更时必须全量重算 | `cz.optimizer.incremental.full.refresh.if.these.tables.change` | 表名列表 |
421
+ | 增量数据量过大时自动切全量 | `cz.optimizer.incremental.full.refresh.if.source.table.changes.significantly` + `threshold` | `true` + `0.5` |
422
+ | SQL 算子多,需要更多状态表加速 | `cz.optimizer.incremental.enable.state.table` | `true`(显式开启) |
423
+ | 不需要状态表,或排查状态表问题 | `cz.optimizer.incremental.enable.state.table` | `false` |
424
+ | 状态表数据损坏,需要重建 | `cz.optimizer.incremental.rebuild.rule.based.state.table` | `true`(一次性) |
425
+ | 刷新间隔长,状态表被提前清理 | `cz.optimizer.incremental.state.table.lifecycle` | 增大至覆盖刷新间隔 |
426
+ | 状态表与业务表隔离管理 | `cz.optimizer.incremental.state.table.specified.schema` | Schema 名 |
427
+ | `CREATE OR REPLACE` 卡住或 SQL 实质性变更 | `cz.sql.mv.check.before.replacing.sql` | `false`(一次性) |
428
+ | 历史分区数据回填或修正 | `cz.optimizer.incremental.backfill.enabled` | `true`(一次性) |
429
+ | 全量刷新时保留历史分区数据 | `cz.optimizer.incremental.full.refresh.overwrite.partitioned.table` | `false` |
@@ -0,0 +1,268 @@
1
+ # Dynamic Table 增量刷新历史查询指南
2
+
3
+ 查看 DT/MV 的增量刷新历史有三种方式,适用于不同场景。
4
+
5
+ ---
6
+
7
+ ## 方式一:SHOW DYNAMIC TABLE REFRESH HISTORY
8
+
9
+ 查看 DT 的刷新作业级别信息,包括每次刷新的状态、耗时、触发方式、刷新模式等。
10
+
11
+ ### 语法
12
+
13
+ ```sql
14
+ -- 查看指定 DT 的刷新历史(使用 WHERE name = 过滤)
15
+ SHOW DYNAMIC TABLE REFRESH HISTORY WHERE name = 'my_dt';
16
+
17
+ -- 限制返回行数
18
+ SHOW DYNAMIC TABLE REFRESH HISTORY WHERE name = 'my_dt' LIMIT 10;
19
+
20
+ -- 组合 WHERE + LIMIT + 状态过滤
21
+ SHOW DYNAMIC TABLE REFRESH HISTORY WHERE name = 'my_dt' AND state = 'SUCCEED' LIMIT 20;
22
+
23
+ -- MV 也支持同样的语法
24
+ SHOW MATERIALIZED VIEW REFRESH HISTORY WHERE name = 'my_mv' LIMIT 10;
25
+ ```
26
+
27
+ > ⚠️ 注意:`FOR <table_name>` 语法在当前版本中可能返回空结果,请使用 `WHERE name = '<table_name>'` 语法。
28
+
29
+ ### 输出列
30
+
31
+ | 列名 | 类型 | 说明 |
32
+ |------|------|------|
33
+ | workspace_name | STRING | 所属 Workspace |
34
+ | schema_name | STRING | 所属 Schema |
35
+ | name | STRING | DT/MV 名称 |
36
+ | virtual_cluster | STRING | 执行刷新的虚拟集群 |
37
+ | start_time | TIMESTAMP | 刷新开始时间 |
38
+ | end_time | TIMESTAMP | 刷新结束时间(运行中为 NULL) |
39
+ | duration | INTERVAL | 刷新耗时(运行中显示已经过的时间) |
40
+ | state | STRING | 刷新状态(SUCCEED / FAILED / RUNNING 等) |
41
+ | refresh_trigger | STRING | 触发方式:`SYSTEM_SCHEDULED`(系统调度自动触发)或 `MANUAL`(用户手动 REFRESH) |
42
+ | refresh_mode | STRING | 刷新模式,见下方详细说明 |
43
+ | error_message | STRING | 失败时的错误信息(成功时为 NULL) |
44
+ | source_tables | ARRAY<MAP<STRING,STRING>> | 源表列表,每个元素是一个 MAP,包含 `workspace`、`schema`、`table_name` 三个 key |
45
+ | stats | MAP<STRING,STRING> | 刷新统计,包含 `rows_inserted`(插入行数)和 `rows_deleted`(删除行数) |
46
+ | job_id | STRING | 对应的 Job ID,可用于关联 `information_schema.job_history` 查更多详情 |
47
+
48
+ ### refresh_mode 详解
49
+
50
+ `refresh_mode` 是判断增量计算是否生效的关键字段:
51
+
52
+ | 值 | 含义 | 说明 |
53
+ |----|------|------|
54
+ | `INCREMENTAL` | 增量刷新 | 增量引擎成功生成了增量计划,只处理了源表的变更数据 |
55
+ | `FULL` | 全量刷新 | 回退到全量重算。可能原因:首次刷新、维度表变更、增量计划生成失败、用户强制全量等 |
56
+ | `NO_DATA` | 无数据变更 | 源表在上次刷新后没有新的数据变更,本次刷新跳过计算 |
57
+
58
+ ### source_tables 详解
59
+
60
+ `source_tables` 列返回该次刷新涉及的所有输入表信息,每个元素是一个 MAP:
61
+
62
+ ```
63
+ [
64
+ {"workspace": "my_ws", "schema": "public", "table_name": "orders"},
65
+ {"workspace": "my_ws", "schema": "public", "table_name": "dim_product"}
66
+ ]
67
+ ```
68
+
69
+ ### stats 详解
70
+
71
+ `stats` 列返回该次刷新对目标表的写入统计:
72
+
73
+ ```
74
+ {"rows_inserted": "1000", "rows_deleted": "50"}
75
+ ```
76
+
77
+ - `rows_inserted`:本次刷新向目标表插入的行数
78
+ - `rows_deleted`:本次刷新从目标表删除的行数(增量模式下,更新操作会产生 delete + insert)
79
+
80
+ ### 典型用法
81
+
82
+ ```sql
83
+ -- 查看最近 5 次刷新是否成功
84
+ SHOW DYNAMIC TABLE REFRESH HISTORY WHERE name = 'my_dt' LIMIT 5;
85
+
86
+ -- 查看失败的刷新记录
87
+ SHOW DYNAMIC TABLE REFRESH HISTORY WHERE name = 'my_dt' AND state = 'FAILED';
88
+
89
+ -- 查看是否回退到了全量刷新(排查增量是否生效)
90
+ SHOW DYNAMIC TABLE REFRESH HISTORY WHERE name = 'my_dt' AND refresh_mode = 'FULL';
91
+
92
+ -- 查看无数据变更的刷新(源表没有新数据时会出现)
93
+ SHOW DYNAMIC TABLE REFRESH HISTORY WHERE name = 'my_dt' AND refresh_mode = 'NO_DATA';
94
+
95
+ -- 查看系统自动调度的刷新
96
+ SHOW DYNAMIC TABLE REFRESH HISTORY WHERE name = 'my_dt' AND refresh_trigger = 'SYSTEM_SCHEDULED';
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 方式二:DESC HISTORY
102
+
103
+ 查看表的版本级别历史,包括每个版本的行数、字节数、操作类型等。适用于了解数据变更粒度。
104
+
105
+ ### 语法
106
+
107
+ ```sql
108
+ -- 查看 DT 的版本历史
109
+ DESC HISTORY my_dt;
110
+
111
+ -- 查看源表的版本历史
112
+ DESC HISTORY source_table;
113
+
114
+ -- 支持 WHERE 过滤
115
+ DESC HISTORY my_dt WHERE version > 10;
116
+
117
+ -- 支持 LIMIT
118
+ DESC HISTORY my_dt LIMIT 20;
119
+ ```
120
+
121
+ ### 输出列
122
+
123
+ 对于普通表(DESC_TABLE_HISTORY):
124
+
125
+ | 列名 | 类型 | 说明 |
126
+ |------|------|------|
127
+ | sequence | BIGINT | 序列号 |
128
+ | version | BIGINT | 版本号 |
129
+ | time | TIMESTAMP | 版本创建时间 |
130
+ | total_rows | BIGINT | 该版本的总行数 |
131
+ | total_bytes | BIGINT | 该版本的总字节数 |
132
+ | user | STRING | 操作用户 |
133
+ | operation | STRING | 操作类型(INSERT / COMPACTION / REFRESH 等) |
134
+ | job_id | STRING | 对应的 Job ID |
135
+
136
+ 对于 DT/MV(DESC_MV_HISTORY),额外包含:
137
+
138
+ | 列名 | 类型 | 说明 |
139
+ |------|------|------|
140
+ | source_tables | ARRAY<MAP<STRING,STRING>> | 源表及其对应的版本信息 |
141
+
142
+ DESC HISTORY 对 DT/MV 的 `source_tables` 比 SHOW REFRESH HISTORY 更详细,包含每个源表在该版本对应的快照信息:
143
+
144
+ ```
145
+ [
146
+ {"table_name": "orders", "workspace": "my_ws", "schema": "public", "version": "123", "sequence": "5", "commit_time": "2025-01-15 10:30:00"},
147
+ {"table_name": "dim_product", "workspace": "my_ws", "schema": "public", "version": "456", "sequence": "2", "commit_time": "2025-01-15 08:00:00"}
148
+ ]
149
+ ```
150
+
151
+ - `version`:源表的 snapshot_id
152
+ - `sequence`:源表的 sequence 号
153
+ - `commit_time`:源表该版本的提交时间
154
+
155
+ 这些信息可以用来追溯某次刷新读取了源表的哪个版本数据。
156
+
157
+ ### 典型用法
158
+
159
+ ```sql
160
+ -- 查看 DT 最近的版本变化,确认 compaction 是否正常执行
161
+ DESC HISTORY my_dt LIMIT 10;
162
+
163
+ -- 查看源表的版本历史,判断数据写入频率
164
+ DESC HISTORY source_table LIMIT 20;
165
+
166
+ -- 查看 DT 的 compaction 记录
167
+ DESC HISTORY my_dt WHERE operation = 'COMPACTION';
168
+ ```
169
+
170
+ ---
171
+
172
+ ## 方式三:information_schema.materialized_view_refresh_history
173
+
174
+ 从 information_schema 查询刷新历史,适合跨表批量分析、与其他系统集成、或做长期趋势监控。数据按天分区(pt_date),保留天数由系统配置决定。
175
+
176
+ ### 语法
177
+
178
+ ```sql
179
+ -- 查看指定 DT 的刷新历史
180
+ SELECT *
181
+ FROM information_schema.materialized_view_refresh_history
182
+ WHERE materialized_view_name = 'my_dt'
183
+ ORDER BY start_time DESC
184
+ LIMIT 10;
185
+
186
+ -- 查看某天所有 DT 的刷新情况
187
+ SELECT materialized_view_name, status, start_time, end_time, error_message
188
+ FROM information_schema.materialized_view_refresh_history
189
+ WHERE pt_date = '2025-01-15'
190
+ ORDER BY start_time DESC;
191
+
192
+ -- 查看失败的刷新
193
+ SELECT materialized_view_name, error_code, error_message, start_time
194
+ FROM information_schema.materialized_view_refresh_history
195
+ WHERE status = 'FAILED' AND pt_date >= '2025-01-01'
196
+ ORDER BY start_time DESC;
197
+ ```
198
+
199
+ ### 输出列
200
+
201
+ | 列名 | 类型 | 说明 |
202
+ |------|------|------|
203
+ | workspace_name | STRING | 所属 Workspace |
204
+ | schema_name | STRING | 所属 Schema |
205
+ | materialized_view_name | STRING | DT/MV 名称 |
206
+ | cru | DOUBLE | 消耗的计算资源单位 |
207
+ | virtual_cluster_name | STRING | 执行刷新的虚拟集群 |
208
+ | status | STRING | 刷新状态 |
209
+ | scheduled_start_time | TIMESTAMP | 计划开始时间 |
210
+ | start_time | TIMESTAMP | 实际开始时间 |
211
+ | end_time | TIMESTAMP | 结束时间 |
212
+ | error_code | STRING | 错误码 |
213
+ | error_message | STRING | 错误信息 |
214
+ | pt_date | STRING | 分区日期 |
215
+
216
+ ### 典型用法
217
+
218
+ ```sql
219
+ -- 统计某个 DT 最近 7 天的刷新成功率
220
+ SELECT
221
+ pt_date,
222
+ COUNT(*) AS total,
223
+ SUM(CASE WHEN status = 'SUCCEED' THEN 1 ELSE 0 END) AS success,
224
+ SUM(CASE WHEN status = 'FAILED' THEN 1 ELSE 0 END) AS failed
225
+ FROM information_schema.materialized_view_refresh_history
226
+ WHERE materialized_view_name = 'my_dt'
227
+ AND pt_date >= DATE_FORMAT(DATEADD(DAY, -7, CURRENT_DATE()), '%Y-%m-%d')
228
+ GROUP BY pt_date
229
+ ORDER BY pt_date;
230
+
231
+ -- 查看消耗 CRU 最多的刷新
232
+ SELECT materialized_view_name, cru, start_time, end_time
233
+ FROM information_schema.materialized_view_refresh_history
234
+ WHERE pt_date >= '2025-01-01'
235
+ ORDER BY cru DESC
236
+ LIMIT 10;
237
+ ```
238
+
239
+ ### 与 information_schema.job_history 的区别
240
+
241
+ `information_schema.job_history` 记录所有类型的 Job(SQL 查询、DML、DDL 等),而 `materialized_view_refresh_history` 专门记录 DT/MV 的刷新历史,字段更有针对性。
242
+
243
+ 如果需要查看刷新 Job 的完整信息(如 job_text、input_bytes 等),可以通过 job_id 关联:
244
+
245
+ ```sql
246
+ -- 通过 SHOW DYNAMIC TABLE REFRESH HISTORY 获取 job_id,再到 job_history 查详情
247
+ SELECT *
248
+ FROM information_schema.job_history
249
+ WHERE job_id = '<从 SHOW REFRESH HISTORY 获取的 job_id>'
250
+ AND pt_date = '2025-01-15';
251
+ ```
252
+
253
+ ---
254
+
255
+ ## 三种方式对比
256
+
257
+ | 特性 | SHOW REFRESH HISTORY | DESC HISTORY | information_schema |
258
+ |------|---------------------|--------------|-------------------|
259
+ | 粒度 | 刷新作业级别 | 表版本级别 | 刷新作业级别 |
260
+ | 刷新模式(增量/全量/无数据) | ✅ refresh_mode | ❌ | ❌ |
261
+ | 触发方式(调度/手动) | ✅ refresh_trigger | ❌ | ❌ |
262
+ | 写入统计(inserted/deleted) | ✅ stats | ❌ | ❌ |
263
+ | 源表列表 | ✅ 表名级别 | ✅ 含版本/sequence/commit_time | ❌ |
264
+ | 版本号/总行数/总字节数 | ❌ | ✅ version/total_rows/total_bytes | ❌ |
265
+ | CRU 消耗 | ❌ | ❌ | ✅ cru |
266
+ | 跨表批量查询 | ❌(单表) | ❌(单表) | ✅(可批量) |
267
+ | compaction 记录 | ❌ | ✅ | ❌ |
268
+ | 适用场景 | 排查增量是否生效、刷新状态 | 查看数据版本变化、追溯源表版本 | 批量分析/监控/CRU 统计 |
@@ -0,0 +1,80 @@
1
+ # Dynamic Table SQL 限制与支持矩阵
2
+
3
+ 本文档列出 Dynamic Table 增量计算支持和不支持的 SQL 模式。
4
+
5
+ ## JOIN 类型支持
6
+
7
+ | JOIN 类型 | 增量支持 | 说明 |
8
+ |-----------|---------|------|
9
+ | INNER JOIN | ✅ | 完全支持 |
10
+ | LEFT JOIN (LEFT OUTER) | ✅ | 完全支持 |
11
+ | RIGHT JOIN (RIGHT OUTER) | ✅ | 完全支持 |
12
+ | FULL OUTER JOIN | ✅ | 完全支持 |
13
+ | LEFT SEMI JOIN | ✅ | 完全支持 |
14
+ | LEFT ANTI JOIN | ✅ | 完全支持 |
15
+
16
+ ## 聚合函数支持
17
+
18
+ ### 支持增量计算的聚合函数
19
+
20
+ - `SUM`, `SUM0`, `COUNT`, `COUNT_IF`, `MIN`, `MAX`, `MIN_BY`, `MAX_BY`
21
+ - `AVG`, `STDDEV_SAMP`, `STDDEV_POP`, `VAR_SAMP`, `VAR_POP`
22
+ - `Percentile`, `Median`, `COUNT_DISTINCT`
23
+ - `BIT_OR`, `BIT_AND`, `BIT_XOR`, `BOOL_OR`, `BOOL_AND`
24
+ - `GROUP_BITMAP` 系列
25
+ - `COLLECT_SET`, `COLLECT_LIST`, `COLLECT_SET_ON_ARRAY`, `COLLECT_LIST_ON_ARRAY`
26
+ - `MAP_AGG`, `WM_CONCAT`
27
+
28
+ ### 结果不稳定的聚合函数(增量结果可能与全量不一致)
29
+
30
+ - `ANY_VALUE`, `FIRST_VALUE`, `LAST_VALUE`
31
+ - `APPROX_COUNT_DISTINCT`, `APPROX_HISTOGRAM`, `APPROX_TOP_K`, `APPROX_PERCENTILE`
32
+ - `JSON_MERGE_AGG`
33
+
34
+ ## 窗口函数支持
35
+
36
+ ### 支持的窗口函数
37
+
38
+ - `ROW_NUMBER`, `RANK`, `DENSE_RANK`, `PERCENT_RANK`
39
+ - `FIRST_VALUE`, `LAST_VALUE`, `NTH_VALUE`
40
+ - `COUNT`, `SUM`, `SUM0`, `MIN`, `MAX`, `AVG`
41
+ - `LEAD`, `LAG`, `CUME_DIST`, `NTILE`
42
+ - `COLLECT_LIST`, `COLLECT_SET`, `COLLECT_SET_ON_ARRAY`, `COLLECT_LIST_ON_ARRAY`
43
+
44
+ ## ORDER BY / LIMIT / OFFSET
45
+
46
+ 支持 `ORDER BY`、`LIMIT`、`OFFSET` 语法。
47
+
48
+ ⚠️ 不建议在 DT 中使用全局 `ORDER BY`。全局排序在每次增量刷新时开销非常大,推荐将排序逻辑放在下游查询数据时执行,而非 ETL 建模阶段。
49
+
50
+ ## 非确定性函数
51
+
52
+ 非确定性函数(如 `NOW()`、`CURRENT_TIMESTAMP`、`CURRENT_DATE`、`random()` 等)在不参与计算逻辑时默认支持。具体来说,只要这些函数不出现在以下位置,就可以正常使用:
53
+ - 窗口函数的 `PARTITION BY` key
54
+ - `JOIN` key
55
+ - `GROUP BY` key
56
+ - 其他函数的入参
57
+
58
+ 典型场景:在 SELECT 中直接输出数据处理时间,记录每条数据被 DT 刷新处理的时刻:
59
+
60
+ ```sql
61
+ CREATE DYNAMIC TABLE order_with_process_time AS
62
+ SELECT
63
+ id,
64
+ amount,
65
+ status,
66
+ CURRENT_TIMESTAMP AS process_time -- 记录刷新时的处理时间,直接输出到目标表
67
+ FROM orders
68
+ WHERE status = 'completed';
69
+ ```
70
+
71
+ 时间函数会在每次 REFRESH 时被常量折叠为当次刷新的时间戳。
72
+
73
+ ## UDF / UDAF / UDTF
74
+
75
+ 自定义函数需要在创建时声明为确定性函数(deterministic),才能在 DT 中使用增量计算。未声明确定性的自定义函数会导致增量计算被禁用。
76
+
77
+ ## 源表类型限制
78
+
79
+ - **虚拟视图(VIEW)**:不能作为 DT 的输入表,会禁用增量计算
80
+ - **外部表(External Table)**:不支持增量计算
@@ -0,0 +1,190 @@
1
+ ---
2
+ name: dynamic-table-alter
3
+ description: |
4
+ 修改 ClickZetta 动态表(Dynamic Table)的结构和属性。支持直接 ALTER 操作(suspend、resume、
5
+ rename_column、set_comment、set_column_comment、set/unset properties)以及 CREATE OR REPLACE
6
+ 重建操作(修改调度周期、计算集群、加列、减列、改列类型、改 SQL 定义)。当用户说"修改动态表"、
7
+ "动态表加列"、"改刷新间隔"、"暂停动态表"时触发。
8
+ ---
9
+
10
+ # 动态表修改工作流
11
+
12
+ ## 指令
13
+
14
+ ### 步骤 1:确认动态表存在并获取当前定义
15
+ 使用 `read_query` 执行 `SHOW CREATE TABLE schema_name.table_name` 获取动态表当前定义。
16
+ 如果不确定是否为动态表,先用 `SHOW TABLES WHERE is_dynamic` 查看列表。
17
+
18
+ ### 步骤 2:判断操作类型并选择执行方式
19
+
20
+ ClickZetta 动态表的修改操作分为两类:
21
+
22
+ **A. 直接 ALTER 操作**(6种,可直接执行):
23
+
24
+ 1. **suspend** — 暂停调度任务:
25
+ ```sql
26
+ ALTER DYNAMIC TABLE dt_name SUSPEND;
27
+ ```
28
+
29
+ 2. **resume** — 启动调度任务:
30
+ ```sql
31
+ ALTER DYNAMIC TABLE dt_name RESUME;
32
+ ```
33
+
34
+ 3. **set_comment** — 修改表注释:
35
+ ```sql
36
+ ALTER DYNAMIC TABLE dt_name SET COMMENT 'comment';
37
+ ```
38
+
39
+ 4. **rename_column** — 修改列名:
40
+ ```sql
41
+ ALTER DYNAMIC TABLE dt_name RENAME COLUMN old_name TO new_name;
42
+ ```
43
+
44
+ 5. **set_column_comment** — 修改列注释(注意用 CHANGE COLUMN):
45
+ ```sql
46
+ ALTER DYNAMIC TABLE dt_name CHANGE COLUMN column_name COMMENT 'comment';
47
+ ```
48
+
49
+ 6. **set/unset properties** — 修改表属性(目前为保留参数):
50
+ ```sql
51
+ -- 设置属性
52
+ ALTER DYNAMIC TABLE dt_name SET PROPERTIES('key' = 'value');
53
+ -- 删除属性
54
+ ALTER DYNAMIC TABLE dt_name UNSET PROPERTIES('key');
55
+ ```
56
+
57
+ **B. CREATE OR REPLACE 操作**(6种,需要重建动态表):
58
+
59
+ > ⚠️ **以下操作不支持 ALTER 语法**。`ALTER DYNAMIC TABLE ... SET REFRESH INTERVAL` 等语法不存在,会报语法错误。必须使用 `CREATE OR REPLACE DYNAMIC TABLE` 重建。
60
+
61
+ 这些操作涉及 SQL 查询逻辑变化,无法通过 ALTER 直接完成:
62
+
63
+ 7. **修改调度周期** — ❌ 不支持 `ALTER ... SET REFRESH INTERVAL`
64
+ 8. **修改计算集群** — ❌ 不支持 `ALTER ... SET VCLUSTER`
65
+ 9. **增加列**
66
+ 10. **减列**
67
+ 11. **修改列类型**
68
+ 12. **修改 SQL 定义**
69
+
70
+ ### 步骤 3:执行 CREATE OR REPLACE 重建(仅 B 类操作)
71
+
72
+ 1. 用 `read_query` 执行 `SHOW CREATE TABLE schema_name.table_name` 获取原始 DDL
73
+ > ⚠️ `SHOW CREATE TABLE` 不支持 LIMIT/WHERE 子句,直接执行即可
74
+ 2. 解析出:列定义、REFRESH 子句、AS SELECT 子句、COMMENT 等
75
+ 3. 根据操作修改对应部分
76
+ 4. 用 `write_query` 执行重建 SQL
77
+
78
+ **关于全量刷新的触发**:
79
+ - 简单的删除列 / 添加列(添加的列只是从源表 SELECT 透传,不参与 JOIN key、GROUP key 等计算)→ **增量刷新**
80
+ - 涉及计算逻辑变化(修改 WHERE 条件、修改聚合逻辑、新增列参与计算等)→ **全量刷新**
81
+ - 兼容类型变更(如 INT → BIGINT)→ **增量刷新**
82
+
83
+ ### 步骤 4:验证修改结果
84
+ 使用 `DESC TABLE dt_name` 确认修改生效。
85
+
86
+ ---
87
+
88
+ ## 示例
89
+
90
+ ### 示例 1:修改调度周期
91
+
92
+ ```sql
93
+ -- 原表
94
+ CREATE DYNAMIC TABLE dt_name
95
+ REFRESH INTERVAL 10 MINUTE vcluster DEFAULT
96
+ AS SELECT * FROM student02;
97
+
98
+ -- 修改后(改为 20 分钟)
99
+ CREATE OR REPLACE DYNAMIC TABLE dt_name
100
+ REFRESH INTERVAL 20 MINUTE vcluster DEFAULT
101
+ AS SELECT * FROM student02;
102
+ ```
103
+
104
+ ### 示例 2:修改计算集群
105
+
106
+ ```sql
107
+ -- 原表
108
+ CREATE DYNAMIC TABLE dt_name
109
+ REFRESH INTERVAL 10 MINUTE vcluster DEFAULT
110
+ AS SELECT * FROM student02;
111
+
112
+ -- 修改后(改为 alter_vc 集群)
113
+ CREATE OR REPLACE DYNAMIC TABLE dt_name
114
+ REFRESH INTERVAL 10 MINUTE vcluster alter_vc
115
+ AS SELECT * FROM student02;
116
+ ```
117
+
118
+ ### 示例 3:增加列
119
+
120
+ ```sql
121
+ -- 原表
122
+ CREATE DYNAMIC TABLE change_table (i, j)
123
+ AS SELECT * FROM dy_base_a;
124
+
125
+ -- 添加一列 col(涉及计算逻辑,下次刷新会全量刷新)
126
+ CREATE OR REPLACE DYNAMIC TABLE change_table (i, j, col)
127
+ AS SELECT i, j, j * 1 FROM dy_base_a;
128
+
129
+ REFRESH DYNAMIC TABLE change_table;
130
+ ```
131
+
132
+ ### 示例 4:减列
133
+
134
+ ```sql
135
+ -- 原表有 i, j 两列
136
+ CREATE DYNAMIC TABLE change_table (i, j)
137
+ AS SELECT * FROM dy_base_a;
138
+
139
+ -- 减列(简单透传,增量刷新)
140
+ CREATE OR REPLACE DYNAMIC TABLE change_table (i)
141
+ AS SELECT i FROM dy_base_a;
142
+ ```
143
+
144
+ ### 示例 5:修改 SQL 定义
145
+
146
+ ```sql
147
+ -- 修改 WHERE 过滤条件(全量刷新)
148
+ CREATE OR REPLACE DYNAMIC TABLE change_table (i, j)
149
+ AS SELECT * FROM dy_base_a WHERE i > 3;
150
+
151
+ REFRESH DYNAMIC TABLE change_table;
152
+ ```
153
+
154
+ ### 示例 6:修改列类型
155
+
156
+ ```sql
157
+ -- INT → BIGINT(兼容类型,增量刷新)
158
+ CREATE OR REPLACE DYNAMIC TABLE change_table (i, j)
159
+ AS SELECT CAST(i AS BIGINT), j FROM dy_base_a;
160
+
161
+ REFRESH DYNAMIC TABLE change_table;
162
+ ```
163
+
164
+ ---
165
+
166
+ ## 平台特有知识
167
+
168
+ - **CHANGE COLUMN 语法**:设置列注释用 `CHANGE COLUMN col COMMENT 'xxx'`,不是 `ALTER COLUMN`
169
+ - **RENAME COLUMN 语法**:`RENAME COLUMN old TO new`
170
+ - **DML 限制**:动态表默认不支持 UPDATE/DELETE/MERGE(因隐藏列 MV__KEY),如需 DML 须先执行 `SET cz.sql.dt.allow.dml = true;`
171
+ - **REFRESH 格式**:`REFRESH INTERVAL <N> MINUTE vcluster <name>`,支持 SECOND/MINUTE/HOUR/DAY
172
+ - **CREATE OR REPLACE 风险**:涉及计算逻辑变化时会触发全量刷新,大表可能耗时较长
173
+ - **schema 前缀**:所有 ALTER/CREATE 语句中表名应包含 schema 前缀
174
+ - **列定义可省略类型**:`CREATE DYNAMIC TABLE dt (i, j) AS SELECT ...` 类型由 SELECT 推断
175
+ - **DROP 语法**:必须用 `DROP DYNAMIC TABLE dt_name`,不能用 `DROP TABLE dt_name`(会报错)
176
+ - **UNDROP 语法**:必须用 `UNDROP TABLE dt_name`,不能用 `UNDROP DYNAMIC TABLE dt_name`
177
+ - **DESC 语法**:动态表用 `DESC TABLE dt_name`,不要写 `DESC DYNAMIC TABLE dt_name EXTENDED`(EXTENDED 不支持)
178
+
179
+ ## 故障排除
180
+
181
+ | 错误 | 原因 | 解决方案 |
182
+ |---|---|---|
183
+ | ALTER 报 "Syntax error at or near 'REFRESH'" | `ALTER ... SET REFRESH INTERVAL` 语法不存在 | 使用 `CREATE OR REPLACE DYNAMIC TABLE ... REFRESH INTERVAL ...` 重建 |
184
+ | ALTER 报 "unsupported operation" | 尝试对动态表执行 B 类操作的 ALTER 语法 | 使用 CREATE OR REPLACE 重建 |
185
+ | `DROP TABLE dt_name` 报错 | 动态表必须用 `DROP DYNAMIC TABLE` | 改为 `DROP DYNAMIC TABLE dt_name` |
186
+ | `UNDROP DYNAMIC TABLE` 报错 | UNDROP 不支持 DYNAMIC TABLE 关键字 | 改为 `UNDROP TABLE dt_name` |
187
+ | `DESC DYNAMIC TABLE ... EXTENDED` 报错 | 不支持 EXTENDED 参数 | 改为 `DESC TABLE dt_name`(不加 EXTENDED) |
188
+ | UPDATE/DELETE 报 "MV__KEY" 相关错误 | 动态表有隐藏列 MV__KEY,默认禁止 DML | 先执行 `SET cz.sql.dt.allow.dml = true;` |
189
+ | CREATE OR REPLACE 后数据为空 | AS SELECT 子句引用的源表或列不正确 | 先用 `read_query` 验证 SELECT 子句 |
190
+ | CREATE OR REPLACE 后全量刷新 | 新增列参与了计算逻辑(JOIN key、GROUP key 等) | 预期行为,等待全量刷新完成 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clickzetta/cz-cli-darwin-x64",
3
- "version": "0.3.10",
3
+ "version": "0.3.13",
4
4
  "description": "cz-cli binary for macOS x64 (Intel)",
5
5
  "os": ["darwin"],
6
6
  "cpu": ["x64"],