@clickzetta/cz-cli-darwin-arm64 0.3.16 → 0.3.18

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.
Files changed (41) hide show
  1. package/bin/cz-cli +0 -0
  2. package/bin/skills/clickzetta-batch-sync-pipeline/SKILL.md +386 -0
  3. package/bin/skills/clickzetta-cdc-sync-pipeline/SKILL.md +548 -0
  4. package/bin/skills/clickzetta-data-ingest-pipeline/SKILL.md +220 -0
  5. package/bin/skills/clickzetta-data-ingest-pipeline/eval_cases.jsonl +5 -0
  6. package/bin/skills/clickzetta-dynamic-table/SKILL.md +112 -0
  7. package/bin/skills/clickzetta-dynamic-table/best-practices/dimension-table-join-guide.md +257 -0
  8. package/bin/skills/clickzetta-dynamic-table/best-practices/medallion-and-stream-patterns.md +124 -0
  9. package/bin/skills/clickzetta-dynamic-table/best-practices/non-partitioned-merge-into-warning.md +96 -0
  10. package/bin/skills/clickzetta-dynamic-table/best-practices/performance-optimization.md +109 -0
  11. package/bin/skills/clickzetta-file-import-pipeline/SKILL.md +156 -0
  12. package/bin/skills/clickzetta-kafka-ingest-pipeline/SKILL.md +751 -0
  13. package/bin/skills/clickzetta-kafka-ingest-pipeline/eval_cases.jsonl +5 -0
  14. package/bin/skills/clickzetta-kafka-ingest-pipeline/references/kafka-pipe-syntax.md +324 -0
  15. package/bin/skills/clickzetta-oss-ingest-pipeline/SKILL.md +537 -0
  16. package/bin/skills/clickzetta-query-optimizer/SKILL.md +156 -0
  17. package/bin/skills/clickzetta-query-optimizer/references/explain.md +56 -0
  18. package/bin/skills/clickzetta-query-optimizer/references/hints-and-sortkey.md +78 -0
  19. package/bin/skills/clickzetta-query-optimizer/references/optimize.md +65 -0
  20. package/bin/skills/clickzetta-query-optimizer/references/result-cache.md +49 -0
  21. package/bin/skills/clickzetta-query-optimizer/references/show-jobs.md +42 -0
  22. package/bin/skills/clickzetta-realtime-sync-pipeline/SKILL.md +276 -0
  23. package/bin/skills/clickzetta-sql-pipeline-manager/SKILL.md +379 -0
  24. package/bin/skills/clickzetta-sql-pipeline-manager/evals/evals.json +166 -0
  25. package/bin/skills/clickzetta-sql-pipeline-manager/references/dynamic-table.md +185 -0
  26. package/bin/skills/clickzetta-sql-pipeline-manager/references/materialized-view.md +129 -0
  27. package/bin/skills/clickzetta-sql-pipeline-manager/references/pipe.md +222 -0
  28. package/bin/skills/clickzetta-sql-pipeline-manager/references/table-stream.md +125 -0
  29. package/bin/skills/clickzetta-table-stream-pipeline/SKILL.md +206 -0
  30. package/bin/skills/clickzetta-vcluster-manager/SKILL.md +212 -0
  31. package/bin/skills/clickzetta-vcluster-manager/references/vc-cache.md +54 -0
  32. package/bin/skills/clickzetta-vcluster-manager/references/vcluster-ddl.md +150 -0
  33. package/bin/skills/clickzetta-volume-manager/SKILL.md +292 -0
  34. package/bin/skills/clickzetta-volume-manager/references/volume-ddl.md +199 -0
  35. package/package.json +1 -1
  36. /package/bin/skills/{dt-creator → clickzetta-dynamic-table/dt-creator}/SKILL.md +0 -0
  37. /package/bin/skills/{dt-creator → clickzetta-dynamic-table/dt-creator}/references/dt-declaration-strategy.md +0 -0
  38. /package/bin/skills/{dt-creator → clickzetta-dynamic-table/dt-creator}/references/incremental-config-reference.md +0 -0
  39. /package/bin/skills/{dt-creator → clickzetta-dynamic-table/dt-creator}/references/refresh-history-guide.md +0 -0
  40. /package/bin/skills/{dt-creator → clickzetta-dynamic-table/dt-creator}/references/sql-limitations.md +0 -0
  41. /package/bin/skills/{dynamic-table-alter → clickzetta-dynamic-table/dynamic-table-alter}/SKILL.md +0 -0
@@ -0,0 +1,124 @@
1
+ # Medallion 架构与 Table Stream 组合模式
2
+
3
+ ## Medallion 三层管道
4
+
5
+ ```
6
+ Bronze(原始数据)
7
+ ↓ Dynamic Table(清洗,INCREMENTAL)
8
+ Silver(清洗数据)
9
+ ↓ Dynamic Table(聚合,FULL)
10
+ Gold(指标数据)
11
+ ↓ BI 工具直接查询
12
+ ```
13
+
14
+ ### Bronze → Silver(增量清洗)
15
+
16
+ ```sql
17
+ -- 前提:源表开启变更跟踪
18
+ ALTER TABLE bronze.raw_orders SET PROPERTIES ('change_tracking' = 'true');
19
+
20
+ CREATE DYNAMIC TABLE IF NOT EXISTS silver.orders_cleaned
21
+ REFRESH INTERVAL 15 MINUTE vcluster default
22
+ AS
23
+ SELECT
24
+ order_id,
25
+ customer_id,
26
+ CAST(amount AS DECIMAL(18,2)) AS amount,
27
+ CAST(created_at AS TIMESTAMP) AS created_at,
28
+ COALESCE(region, 'unknown') AS region
29
+ FROM bronze.raw_orders
30
+ WHERE order_id IS NOT NULL AND amount > 0;
31
+ ```
32
+
33
+ ### Silver → Gold(聚合指标,通常 FULL)
34
+
35
+ ```sql
36
+ CREATE DYNAMIC TABLE IF NOT EXISTS gold.orders_daily_summary
37
+ REFRESH INTERVAL 60 MINUTE vcluster default
38
+ AS
39
+ SELECT
40
+ DATE(created_at) AS stat_date,
41
+ region,
42
+ COUNT(*) AS order_count,
43
+ SUM(amount) AS total_revenue,
44
+ COUNT(DISTINCT customer_id) AS unique_customers
45
+ FROM silver.orders_cleaned
46
+ GROUP BY 1, 2;
47
+ ```
48
+
49
+ ---
50
+
51
+ ## 与 Table Stream 组合(事件驱动)
52
+
53
+ Table Stream 捕获源表变更,Dynamic Table 消费 Stream 做增量处理。
54
+
55
+ ### 基本模式
56
+
57
+ ```sql
58
+ -- 1. 源表开启变更跟踪
59
+ ALTER TABLE bronze.raw_orders SET PROPERTIES ('change_tracking' = 'true');
60
+
61
+ -- 2. 创建 Table Stream
62
+ CREATE TABLE STREAM bronze.orders_stream
63
+ ON TABLE bronze.raw_orders
64
+ WITH PROPERTIES ('TABLE_STREAM_MODE' = 'STANDARD');
65
+
66
+ -- 3. Dynamic Table 消费 Stream
67
+ -- 注意:Stream 作为 DT 源时,每次刷新会消费 offset
68
+ CREATE DYNAMIC TABLE IF NOT EXISTS silver.orders_incremental
69
+ REFRESH INTERVAL 5 MINUTE vcluster default
70
+ AS
71
+ SELECT order_id, customer_id, amount, status
72
+ FROM bronze.orders_stream
73
+ WHERE __change_type IN ('INSERT', 'UPDATE_AFTER');
74
+ ```
75
+
76
+ ### MERGE INTO + Table Stream(替代非分区 DT 的去重场景)
77
+
78
+ 当需要按主键去重且源表持续写入时,推荐用 MERGE INTO 替代 Dynamic Table:
79
+
80
+ ```sql
81
+ -- 1. 创建 Table Stream
82
+ CREATE TABLE STREAM source_stream ON TABLE source_table
83
+ WITH PROPERTIES ('TABLE_STREAM_MODE' = 'STANDARD', 'SHOW_INITIAL_ROWS' = 'TRUE');
84
+
85
+ -- 2. 创建目标表
86
+ CREATE TABLE target_table (
87
+ id BIGINT,
88
+ col1 STRING,
89
+ col2 INT,
90
+ event_time TIMESTAMP
91
+ );
92
+
93
+ -- 3. 定时调度 MERGE INTO 消费 Stream
94
+ MERGE INTO target_table t
95
+ USING (
96
+ SELECT id, col1, col2, event_time,
97
+ CASE WHEN `value` IS NULL OR `value` = '' THEN 'DELETE' ELSE 'UPSERT' END AS op
98
+ FROM source_stream
99
+ ) s ON t.id = s.id
100
+ WHEN MATCHED AND s.op = 'UPSERT' THEN UPDATE SET
101
+ t.col1 = s.col1, t.col2 = s.col2, t.event_time = s.event_time
102
+ WHEN NOT MATCHED AND s.op = 'UPSERT' THEN INSERT
103
+ (id, col1, col2, event_time) VALUES (s.id, s.col1, s.col2, s.event_time);
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 实时报表物化
109
+
110
+ ```sql
111
+ -- 每小时刷新销售汇总,供 BI 工具直接查询
112
+ CREATE DYNAMIC TABLE IF NOT EXISTS rpt.sales_hourly
113
+ REFRESH INTERVAL 60 MINUTE vcluster default
114
+ AS
115
+ SELECT
116
+ DATE_TRUNC('hour', order_time) AS hour_bucket,
117
+ product_category,
118
+ SUM(amount) AS revenue,
119
+ COUNT(*) AS order_cnt,
120
+ AVG(amount) AS avg_order_value
121
+ FROM silver.orders_cleaned
122
+ WHERE order_time >= DATEADD(day, -30, CURRENT_DATE)
123
+ GROUP BY 1, 2;
124
+ ```
@@ -0,0 +1,96 @@
1
+ # 非分区表 + 持续写入:DT 风险告警与 MERGE INTO 替代建议
2
+
3
+ ## 触发条件
4
+
5
+ 当用户要创建的 DT 同时满足以下条件时,**必须向用户发出告警**:
6
+
7
+ 1. DT 本身是非分区表(没有 `PARTITIONED BY`,也没有 `SESSION_CONFIGS()` 引用)
8
+ 2. 源表也是非分区表,且数据会持续写入(如 Kafka 消费落地表、CDC 明细表)
9
+ 3. SQL 中包含按主键去重的窗口函数模式:`ROW_NUMBER() OVER (PARTITION BY key ORDER BY ts DESC) WHERE rn = 1`
10
+
11
+ ## 告警内容
12
+
13
+ 向用户说明以下三个风险:
14
+
15
+ ### 风险 1:存储无限膨胀
16
+
17
+ 非分区的 DT 和非分区的源表都没有自动的数据生命周期管理机制(`data_lifecycle` 仅对分区表生效)。随着数据持续写入:
18
+ - 源表数据量无限增长
19
+ - DT 的状态表全局维护,随数据量线性膨胀
20
+ - 目标表数据量同步增长
21
+ - 三者叠加,存储成本持续上升且不可控
22
+
23
+ ### 风险 2:源表归档引发性能灾难
24
+
25
+ 当存储膨胀到一定程度,运维人员通常会对源表进行归档——将历史数据迁移到冷存储或归档表,然后从源表中删除以释放空间。此时:
26
+
27
+ - DT 会捕获源表的删除事件,并将其反映到增量计算结果中
28
+ - `ROW_NUMBER() OVER (PARTITION BY key ORDER BY ts DESC) WHERE rn = 1` 的删除处理代价极高:
29
+ - 窗口函数无法增量处理删除——需要回读该 key 下的所有历史数据重新排序
30
+ - 非分区表没有分区边界来限制回读范围,可能需要扫描整张表
31
+ - 大规模归档会产生海量删除变更,每个 key 都需要独立回算
32
+ - 一次源表归档可能导致 DT REFRESH 耗时从秒级飙升到小时级甚至失败
33
+
34
+ ### 风险 3:无法过滤归档产生的删除事件
35
+
36
+ DT 的增量引擎自动捕获源表的所有变更(INSERT / UPDATE / DELETE),用户无法干预这个过程。SQL 中的 `WHERE op <> 'DELETE'` 过滤的是业务层面的删除标记,而不是源表物理删除产生的删除变更。用户没有任何手段告诉 DT "这些删除是归档操作,请忽略"。
37
+
38
+ ## 推荐替代方案
39
+
40
+ 建议用户使用 MERGE INTO + Table Stream 替代:
41
+
42
+ ```sql
43
+ -- Step 1: 源表开启变更跟踪
44
+ ALTER TABLE source_table SET PROPERTIES ('change_tracking' = 'true');
45
+
46
+ -- Step 2: 创建 Table Stream
47
+ CREATE TABLE STREAM source_stream ON TABLE source_table
48
+ WITH (TABLE_STREAM_MODE = 'STANDARD', SHOW_INITIAL_ROWS = TRUE);
49
+
50
+ -- Step 3: 创建目标表
51
+ CREATE TABLE target_table (
52
+ id BIGINT,
53
+ col1 STRING,
54
+ col2 INT,
55
+ event_time TIMESTAMP
56
+ );
57
+
58
+ -- Step 4: 定时调度 MERGE INTO 消费 Stream
59
+ MERGE INTO target_table t
60
+ USING (
61
+ SELECT id, col1, col2, event_time,
62
+ CASE WHEN `value` IS NULL OR `value` = '' THEN 'DELETE' ELSE 'UPSERT' END AS op
63
+ FROM source_stream
64
+ ) s ON t.id = s.id
65
+ WHEN MATCHED AND s.op = 'UPSERT' THEN UPDATE SET
66
+ t.col1 = s.col1, t.col2 = s.col2, t.event_time = s.event_time
67
+ WHEN NOT MATCHED AND s.op = 'UPSERT' THEN INSERT
68
+ (id, col1, col2, event_time) VALUES (s.id, s.col1, s.col2, s.event_time);
69
+ ```
70
+
71
+ MERGE INTO + Table Stream 的优势:
72
+ - **每次计算独立**:只消费 Stream 中的增量数据,不依赖源表全量状态
73
+ - **归档免疫**:源表归档时,可在 USING 子查询中通过 WHERE 条件过滤归档产生的删除事件
74
+ - **目标表独立管理**:目标表的生命周期与源表解耦,可独立制定归档策略
75
+ - **offset 自动推进**:MERGE INTO 消费 Stream 后 offset 自动推进,下次只处理新变更
76
+
77
+ ## 告警话术模板
78
+
79
+ 当检测到用户的 DT 满足触发条件时,使用以下话术:
80
+
81
+ > ⚠️ **风险提示**:您正在创建一个非分区的 Dynamic Table,且源表也是非分区的持续写入表。这种组合存在以下长期运维风险:
82
+ >
83
+ > 1. **存储无限膨胀**:源表、DT 目标表、DT 状态表三者都会持续增长,且无法通过 `data_lifecycle` 自动清理
84
+ > 2. **源表归档会引发性能灾难**:当您需要对源表进行归档(迁移历史数据后删除)时,DT 会捕获这些删除事件。由于 SQL 中包含 `ROW_NUMBER() ... WHERE rn = 1` 的去重逻辑,每个被删除的 key 都需要回读历史数据重新排序,非分区表没有边界限制,可能导致 REFRESH 性能严重回退
85
+ > 3. **无法过滤归档删除**:DT 增量引擎自动捕获源表所有变更,您无法告诉 DT 忽略归档操作产生的删除
86
+ >
87
+ > **建议**:对于这类"非分区 CDC 明细表合并为结果表"的场景,推荐使用 MERGE INTO + Table Stream 方案。每次只消费增量数据,源表归档时可通过 WHERE 条件过滤删除事件,不会影响下游。
88
+
89
+ ## 判断逻辑
90
+
91
+ 在帮助用户创建 DT 时,按以下顺序检查:
92
+
93
+ 1. DT 是否有 `PARTITIONED BY` 或 `SESSION_CONFIGS()` → 如果有,不触发告警
94
+ 2. 源表是否是持续写入的非分区表(如 Kafka 消费表、CDC 明细表)→ 如果不是,不触发告警
95
+ 3. SQL 是否包含 `ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ... DESC) WHERE rn = 1` 模式 → 如果包含,风险最高,必须告警
96
+ 4. 即使没有 ROW_NUMBER,只要满足条件 1+2,也应提醒用户注意存储膨胀风险
@@ -0,0 +1,109 @@
1
+ # Dynamic Table 性能优化指南
2
+
3
+ 本文档从 SQL 写法、数据特征、管道设计三个维度,帮助用户写出增量刷新性能更好的 DT。
4
+
5
+ ## 核心原则:增量刷新的代价模型
6
+
7
+ 增量刷新的性能取决于三个因素:
8
+ 1. **变更量占比**:每次刷新时,源表中有多少数据发生了变化。变更量越小,增量越划算
9
+ 2. **算子类型**:不同 SQL 算子的增量代价差异很大
10
+ 3. **数据局部性**:变更数据在 JOIN key / GROUP BY key / PARTITION BY key 上的分布是否集中
11
+
12
+ 当变更量超过总数据量的较大比例时,增量刷新可能反而比全量刷新更慢,因为增量需要额外的变更数据计算、去重合并、状态表读写等开销。
13
+
14
+ ## SQL 写法优化
15
+
16
+ ### 1. 优先使用 INNER JOIN 而非 OUTER JOIN
17
+
18
+ INNER JOIN 的增量计算比 OUTER JOIN 更高效:
19
+ - INNER JOIN:只需要计算 A 的变更数据 JOIN B 的全量数据 + A 的全量数据 JOIN B 的变更数据
20
+ - LEFT/RIGHT/FULL OUTER JOIN:还需要额外处理 NULL 填充、反向撤回等逻辑
21
+
22
+ 如果业务上可以保证参照完整性(即 JOIN key 一定能匹配),优先用 INNER JOIN。
23
+
24
+ ```sql
25
+ -- ❌ 不必要的 LEFT JOIN(如果 product 一定存在)
26
+ SELECT o.*, p.name FROM orders o LEFT JOIN products p ON o.pid = p.id;
27
+
28
+ -- ✅ 改用 INNER JOIN
29
+ SELECT o.*, p.name FROM orders o INNER JOIN products p ON o.pid = p.id;
30
+ ```
31
+
32
+ ### 2. 减少不必要的 DISTINCT
33
+
34
+ 每次增量刷新时,DISTINCT 需要对受影响的 key 做重算。如果上游数据已经去重,或者可以通过其他方式保证唯一性,去掉 DISTINCT。
35
+
36
+ ```sql
37
+ -- ❌ 冗余的 DISTINCT
38
+ SELECT DISTINCT user_id, user_name FROM user_events;
39
+ ```
40
+
41
+ ### 3. 窗口函数必须有 PARTITION BY
42
+
43
+ 没有 PARTITION BY 的窗口函数会导致每次增量刷新都全量重算整个窗口。加上 PARTITION BY 后,只需要重算受影响的分区。
44
+
45
+ ```sql
46
+ -- ❌ 全局窗口,每次增量都全量重算
47
+ SELECT *, ROW_NUMBER() OVER (ORDER BY created_at DESC) AS rn FROM events;
48
+
49
+ -- ✅ 加上 PARTITION BY,只重算有变更的分区
50
+ SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY created_at DESC) AS rn FROM events;
51
+ ```
52
+
53
+ ### 4. 聚合 key 尽量使用简单列引用
54
+
55
+ 复合表达式作为 GROUP BY key 会降低增量效率,因为引擎需要对表达式求值后才能判断哪些 key 受影响。
56
+
57
+ ```sql
58
+ -- ❌ 复合表达式作为 GROUP BY key
59
+ SELECT DATE_TRUNC('hour', ts) AS hour, SUM(amount)
60
+ FROM transactions
61
+ GROUP BY DATE_TRUNC('hour', ts);
62
+
63
+ -- ✅ 如果可能,在上游预计算好 key 列
64
+ -- 或者拆分为两个 DT(见下文"管道拆分")
65
+ ```
66
+
67
+ ### 5. 尽可能使用分区条件限制数据范围
68
+
69
+ 在 DT 的 SQL 中对源表添加分区过滤条件,可以显著减少每次增量刷新需要扫描的数据量。
70
+
71
+ ```sql
72
+ -- ❌ 不加分区条件,每次扫描全表
73
+ SELECT o.*, p.name
74
+ FROM orders o JOIN products p ON o.pid = p.id;
75
+
76
+ -- ✅ 通过分区条件限制数据范围
77
+ SELECT o.*, p.name
78
+ FROM orders o JOIN products p ON o.pid = p.id
79
+ WHERE o.ds = SESSION_CONFIGS()['dt.args.ds'];
80
+ ```
81
+
82
+ ## 管道拆分:复杂 DT 拆成多级
83
+
84
+ 当一个 DT 的 SQL 包含多个 JOIN + 聚合 + 窗口函数时,考虑拆分为多个 DT,每个 DT 只做一件事。
85
+
86
+ 好处:
87
+ - 每个 DT 的增量计算更简单、更快
88
+ - 中间 DT 可以被多个下游 DT 复用
89
+ - 出问题时更容易定位是哪一层的问题
90
+ - 不同层可以使用不同的优化策略
91
+
92
+ ## 数据特征与增量效率
93
+
94
+ ### 变更量占比
95
+
96
+ 增量刷新在变更量占总数据量比例较小时效果最好。经验值:
97
+ - < 5%:增量刷新通常显著优于全量
98
+ - 5% ~ 20%:取决于具体算子和数据分布
99
+ - \> 20%:可能需要评估是否全量刷新更合适
100
+
101
+ ### Append-Only 源表
102
+
103
+ 如果源表只有 INSERT 没有 UPDATE/DELETE 可以显著优化:
104
+ - 增量引擎知道变更数据只有新增(无撤回),可以跳过去重合并等操作
105
+ - 聚合可以直接累加,不需要维护完整的中间状态
106
+
107
+ ### 变更数据的分布
108
+
109
+ 如果变更数据集中在少数 key 上(如最近时间段的数据),增量效率高。如果变更分散在大量 key 上,聚合和窗口函数需要重算大量分区,效率下降。
@@ -0,0 +1,156 @@
1
+ ---
2
+ name: clickzetta-file-import-pipeline
3
+ description: |
4
+ 从 URL、本地文件或 Volume 路径将数据导入到 ClickZetta 表中,覆盖文件下载、格式推断、
5
+ 表创建、COPY INTO 导入、结果验证的完整流程。当用户说"导入数据"、"从 URL 加载"、
6
+ "上传 CSV 到表"、"文件导入"、"COPY INTO"时触发。包含 ClickZetta USER VOLUME 机制、
7
+ COPY INTO 语法、格式推断规则、写入模式语义等平台特有知识。
8
+ Keywords: file import, URL, CSV, JSON, Parquet, COPY INTO, Volume
9
+ ---
10
+
11
+ # URL/文件数据导入工作流
12
+
13
+ ## 指令
14
+
15
+ ### 步骤 1:获取源文件并上传到 Volume
16
+ 根据数据来源选择对应方式:
17
+ - **HTTP/HTTPS URL**:需要先用外部工具下载到本地,然后用 `PUT` 命令上传到 User Volume
18
+ - **本地文件**:执行 SQL `PUT '/local/path/file.csv' TO USER VOLUME` 上传
19
+ - **Volume 路径**:文件已在 Volume 上,跳过此步骤
20
+ - **外部 Volume(OSS/S3/COS)**:文件已在外部 Volume,直接使用
21
+ - 记录上传后的 Volume 名称和文件名,后续步骤需要
22
+
23
+ > ⚠️ **注意**:文件上传操作参考 `clickzetta-volume-manager` skill。
24
+
25
+ ### 步骤 2:推断文件格式
26
+ 根据文件扩展名推断格式(ClickZetta COPY INTO 支持的格式):
27
+ - `.csv`, `.tsv`, `.txt` → CSV 格式
28
+ - `.json`, `.jsonl`, `.ndjson` → JSON 格式
29
+ - `.parquet`, `.pq` → PARQUET 格式
30
+ - `.orc` → ORC 格式
31
+ - `.bson` → BSON 格式
32
+ 如果扩展名不明确,执行 `SELECT FROM VOLUME ... USING format` 预览文件内容来确认格式和 schema。
33
+
34
+ ### 步骤 3:确认或创建目标表
35
+ 根据写入模式处理目标表:
36
+ - **create 模式**:表必须不存在。执行 `SELECT FROM VOLUME ... LIMIT 5` 推断 schema,然后执行 `CREATE TABLE` 创建表
37
+ - **append 模式**:表必须已存在。用 `DESC TABLE <table_name>` 确认表存在并检查列兼容性
38
+ - **overwrite 模式**:表存在则先清空。执行 `TRUNCATE TABLE table_name`,再执行 COPY INTO(⚠️ 不支持 `COPY OVERWRITE INTO` 语法)
39
+
40
+ ### 步骤 4:执行 COPY INTO 导入数据
41
+ 执行 COPY INTO 语句。核心语法:
42
+
43
+ ```sql
44
+ COPY INTO target_table
45
+ FROM VOLUME volume_name
46
+ USING format_type
47
+ OPTIONS('option_name' = 'value')
48
+ FILES('filename');
49
+ ```
50
+
51
+ 对于 USER VOLUME(通过 PUT 命令上传的文件):
52
+ ```sql
53
+ COPY INTO target_table
54
+ FROM USER VOLUME
55
+ USING CSV
56
+ OPTIONS('header' = 'true')
57
+ FILES('uploaded_filename');
58
+ ```
59
+
60
+ CSV 格式可附加 OPTIONS:
61
+ ```sql
62
+ COPY INTO target_table
63
+ FROM VOLUME vol
64
+ USING CSV
65
+ OPTIONS('header' = 'true', 'sep' = ',', 'quote' = '"', 'nullValue' = '')
66
+ FILES('data.csv');
67
+ ```
68
+
69
+ ⚠️ **语法顺序要求**:`OPTIONS` 必须在 `FILES` 之前,否则报错 `Syntax error - missing EQ at '('`
70
+
71
+ overwrite 模式(⚠️ 不支持 `COPY OVERWRITE INTO`):
72
+ ```sql
73
+ -- 正确方式:先 TRUNCATE 再 COPY
74
+ TRUNCATE TABLE target_table;
75
+ COPY INTO target_table FROM VOLUME vol USING CSV FILES('data.csv');
76
+ ```
77
+
78
+ ### 步骤 5:验证导入结果
79
+ 执行验证查询:
80
+ ```sql
81
+ SELECT COUNT(*) as row_count FROM target_table;
82
+ SELECT * FROM target_table LIMIT 5;
83
+ ```
84
+ 确认行数符合预期,数据内容正确。
85
+
86
+ ## 示例
87
+
88
+ ### 示例 1:从 URL 导入 CSV 到新表
89
+ ```sql
90
+ -- 1. 下载 URL 文件到本地,然后上传到 User Volume
91
+ PUT '/tmp/data.csv' TO USER VOLUME;
92
+
93
+ -- 2. 预览文件内容推断 schema
94
+ SELECT * FROM USER VOLUME USING CSV OPTIONS('header' = 'true') FILES('data.csv') LIMIT 5;
95
+ -- 推断出列:id INT, name STRING, value DOUBLE
96
+
97
+ -- 3. 创建目标表
98
+ CREATE TABLE imported_data (id INT, name STRING, value DOUBLE);
99
+
100
+ -- 4. 执行 COPY INTO 导入(注意:OPTIONS 必须在 FILES 之前)
101
+ COPY INTO imported_data FROM USER VOLUME USING CSV OPTIONS('header' = 'true') FILES('data.csv');
102
+
103
+ -- 5. 验证导入结果
104
+ SELECT COUNT(*) FROM imported_data;
105
+ ```
106
+
107
+ ### 示例 2:追加 Parquet 数据到已有表
108
+ ```sql
109
+ -- 1. 上传本地文件到 User Volume
110
+ PUT '/local/new_batch.parquet' TO USER VOLUME;
111
+
112
+ -- 2. 确认目标表存在
113
+ DESC TABLE existing_table;
114
+
115
+ -- 3. 执行 COPY INTO 导入(Parquet 格式通常不需要 OPTIONS)
116
+ COPY INTO existing_table FROM USER VOLUME USING PARQUET FILES('new_batch.parquet');
117
+
118
+ -- 4. 验证导入结果
119
+ SELECT COUNT(*) FROM existing_table;
120
+ ```
121
+
122
+ ### 示例 3:从外部 Volume(OSS)导入
123
+ ```sql
124
+ -- 1. 查看 Volume 中的文件列表
125
+ SHOW VOLUME DIRECTORY my_oss_volume;
126
+
127
+ -- 2. 预览文件内容
128
+ SELECT * FROM VOLUME my_oss_volume USING CSV OPTIONS('header' = 'true') FILES('data.csv') LIMIT 5;
129
+
130
+ -- 3. 创建目标表并导入(注意:OPTIONS 必须在 FILES 之前)
131
+ CREATE TABLE imported_data (col1 INT, col2 STRING);
132
+ COPY INTO imported_data FROM VOLUME my_oss_volume USING CSV OPTIONS('header' = 'true') FILES('data.csv');
133
+ ```
134
+
135
+ ## 故障排除
136
+
137
+ | 错误 | 原因 | 解决方案 |
138
+ |------|------|----------|
139
+ | COPY INTO 报 "table not found" | create 模式下表未创建,或 append 模式下表名拼写错误 | 先用 `SHOW TABLES` 确认表是否存在 |
140
+ | COPY INTO 报 "file not found" | FILES 中的文件名与 Volume 上的实际文件名不匹配 | 执行 `SHOW VOLUME DIRECTORY vol_name` 或 `SHOW USER VOLUME DIRECTORY` 确认文件名,注意大小写敏感 |
141
+ | COPY INTO 报语法错误 "missing EQ at '('" | OPTIONS 放在了 FILES 之后 | 调整顺序,确保 `OPTIONS` 在 `FILES` 之前:`USING CSV OPTIONS(...) FILES(...)` |
142
+ | CSV 导入列数不匹配 | CSV 文件有 header 行但未指定 `OPTIONS('header'='true')`,导致 header 被当作数据行 | 添加 `OPTIONS('header' = 'true')`,或检查 CSV 分隔符是否正确(sep 参数) |
143
+ | COPY INTO 报 "schema mismatch" | 文件中的数据类型与目标表列定义不兼容 | 执行 `SELECT FROM VOLUME ... USING format LIMIT 5` 预览实际数据,调整表定义或使用列映射 |
144
+ | overwrite 模式数据未清空 | 使用了 `COPY OVERWRITE INTO` 语法(不支持) | overwrite 模式应先用 `TRUNCATE TABLE` 清空表,再执行 `COPY INTO` |
145
+ | SELECT FROM VOLUME 报错 | 格式不匹配或多格式文件混合 | 确认 USING 后的格式与实际文件格式一致;使用 `FILES()` 指定文件或 `SUBDIRECTORY` 指定子目录 |
146
+ | PUT 命令失败 | 本地文件路径不存在 | 确认本地文件路径正确,文件存在 |
147
+
148
+ ---
149
+
150
+ ## 依赖的 Skills
151
+
152
+ | 操作 | 需要加载的 Skill |
153
+ |------|-----------------|
154
+ | 文件上传/下载/删除 | `clickzetta-volume-manager` |
155
+ | 查询 Volume 文件内容 | `clickzetta-volume-manager` |
156
+ | COPY INTO 导入 | 本 Skill |