Skip to content

Commit 248dc85

Browse files
authored
docs: update schema evolution copy options (#3430)
1 parent d417ed8 commit 248dc85

4 files changed

Lines changed: 373 additions & 23 deletions

File tree

docs/cn/guides/40-load-data/04-transform/07-schema-evolution.md

Lines changed: 140 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ sidebar_label: Schema Evolution
66

77
# Schema Evolution
88

9-
Schema Evolution 允许 Databend 在 `COPY INTO` 加载数据时,自动将源 Parquet 文件中存在但表中缺失的列添加到目标表
9+
Schema Evolution 允许 Databend 在 `COPY INTO` 加载数据时,自动将源文件中存在但目标表中缺失的列添加到目标表。目前支持 **Parquet****NDJSON**
1010

1111
## 工作原理
1212

13-
启用后,`COPY INTO` 会:
13+
启用后,Databend 会在加载前推断源文件 schema,并将新列追加到表末尾。新列为可空列,缺失值会填充为 `NULL`
1414

15-
1. 从源 Parquet 文件推断 schema。
16-
2. 将新列(表中不存在的)作为可空列添加到表中。
17-
3. 加载数据,缺失的值用 `NULL` 填充。
15+
不同文件格式的用法略有差异:
16+
17+
- **Parquet**:启用表选项后,`COPY INTO` 会直接从 Parquet 文件 schema 推断新列。
18+
- **NDJSON**:启用表选项后,`COPY INTO` 会使用 `AUTO` 采样值进行 schema 推断。可以选择设置 `SCHEMA_EVOLUTION = (...)` 来覆盖文件和记录采样限制。
1819

1920
## 启用 Schema Evolution
2021

@@ -34,9 +35,15 @@ CREATE TABLE my_table(id INT) ENABLE_SCHEMA_EVOLUTION = true;
3435
ALTER TABLE my_table SET OPTIONS(ENABLE_SCHEMA_EVOLUTION = false);
3536
```
3637

37-
## 教程
38+
## 权限要求
39+
40+
`COPY INTO <table>` 从 Stage 或外部位置加载文件,并触发 Schema Evolution 推断时,执行加载的角色需要拥有目标表的 `INSERT``ALTER` 权限。`ALTER` 权限用于允许 Databend 在加载前自动追加新列。
41+
42+
基于查询的 COPY 不受此限制,例如 `COPY INTO <table> FROM (SELECT ... FROM @stage)`,仍按原有权限要求检查。
43+
44+
## Parquet 示例
3845

39-
以下是一个完整可运行的示例
46+
以下示例展示如何从不同 schema 的 Parquet 文件加载数据,并自动添加缺失列
4047

4148
### 步骤 1:创建表和 Stage
4249

@@ -73,7 +80,7 @@ FILE_FORMAT = (TYPE = parquet MISSING_FIELD_AS = FIELD_DEFAULT);
7380

7481
### 步骤 4:验证结果
7582

76-
表现在有三列`amount``currency` 被自动添加:
83+
表现在有三列`amount``currency` 被自动添加:
7784

7885
```sql
7986
DESC invoices;
@@ -105,6 +112,129 @@ SELECT * FROM invoices ORDER BY order_id;
105112

106113
第 3 行的 `currency = NULL`,因为其源文件中不包含该列。
107114

115+
## NDJSON 示例
116+
117+
Databend 使用 `TYPE = ndjson` 加载 NDJSON 文件。NDJSON 文件没有像 Parquet 那样的内嵌列式 schema,Databend 会先对文件内容进行采样,推断目标表中缺失的字段,再追加为可空列。
118+
119+
### 步骤 1:创建表和 Stage
120+
121+
```sql
122+
CREATE OR REPLACE TABLE events(id INT);
123+
CREATE OR REPLACE STAGE events_stage;
124+
```
125+
126+
### 步骤 2:生成不同字段的 NDJSON 文件
127+
128+
```sql
129+
-- 文件包含字段:id, city, score
130+
COPY INTO @events_stage FROM (
131+
SELECT 1 AS id, 'SF' AS city, 9 AS score
132+
UNION ALL
133+
SELECT 2, 'NYC', 8
134+
) FILE_FORMAT = (TYPE = ndjson);
135+
136+
-- 文件包含字段:id, score(无 city)
137+
COPY INTO @events_stage FROM (
138+
SELECT 3 AS id, 7 AS score
139+
) FILE_FORMAT = (TYPE = ndjson);
140+
```
141+
142+
### 步骤 3:启用 Schema Evolution 并加载
143+
144+
```sql
145+
ALTER TABLE events SET OPTIONS(ENABLE_SCHEMA_EVOLUTION = true);
146+
147+
COPY INTO events
148+
FROM @events_stage/
149+
FILE_FORMAT = (TYPE = ndjson MISSING_FIELD_AS = FIELD_DEFAULT)
150+
SCHEMA_EVOLUTION = (
151+
SAMPLE_FILES = AUTO,
152+
SAMPLE_RECORDS_PER_FILE = AUTO,
153+
SAMPLE_TOTAL_RECORDS = AUTO
154+
);
155+
```
156+
157+
`SCHEMA_EVOLUTION` 的三个采样选项都可以设置为 `AUTO` 或正整数:
158+
159+
| 选项 | 说明 |
160+
|------|------|
161+
| `SAMPLE_FILES` | 采样的文件数量。 |
162+
| `SAMPLE_RECORDS_PER_FILE` | 每个采样文件中最多采样的记录数。 |
163+
| `SAMPLE_TOTAL_RECORDS` | 所有采样文件中最多采样的记录总数。 |
164+
165+
如果省略 `SCHEMA_EVOLUTION`,Databend 会对三个采样选项都使用 `AUTO`。当前 `AUTO` 行为最多采样 64 个文件、每个文件 1,000 条记录、总计 10,000 条记录。这些内部默认值未来版本可能会调整。如果加载结果对采样策略敏感,建议显式设置 `SAMPLE_FILES``SAMPLE_RECORDS_PER_FILE``SAMPLE_TOTAL_RECORDS`
166+
167+
### NDJSON 推断规则
168+
169+
对 NDJSON 执行 Schema Evolution 时,Databend 会按以下规则推断新列:
170+
171+
- 只从采样到的 NDJSON 记录中推断 schema;未被采样覆盖的字段不会提前加入目标表。
172+
- 每行必须是一个 JSON 对象。Databend 会将对象的顶层字段名作为候选列名。
173+
- 目标表中已存在的列不会重复添加;只追加目标表中缺失的新字段。
174+
- 新字段类型由采样记录中的 JSON 值推断而来,例如整数、浮点数、字符串和布尔值会推断为对应的数据类型。
175+
- Schema Evolution 对 NDJSON 使用浅层推断:顶层字段值如果是对象或数组,会作为 `VARIANT` 列追加,不会递归展开为嵌套列。
176+
- `NULL` 样本只表示该字段可为空,不会把后续非空类型强制改成 `VARCHAR``VARIANT`
177+
- 多个文件或多条记录中的同名字段会合并类型:整数和浮点数冲突时合并为 `DOUBLE`;其他标量类型冲突时合并为 `VARCHAR`;任何涉及对象、数组或 `VARIANT` 的冲突都会合并为 `VARIANT`
178+
- 如果实际加载时遇到采样阶段没有推断出的额外字段,加载会失败并返回这些字段名。此时需要调大 `SAMPLE_FILES``SAMPLE_RECORDS_PER_FILE``SAMPLE_TOTAL_RECORDS`
179+
180+
:::note
181+
`INFER_SCHEMA` 表函数对 NDJSON 默认不限制嵌套深度;这里描述的是 `COPY INTO` Schema Evolution 的浅层推断规则。
182+
:::
183+
184+
例如,下面的 NDJSON 记录会推断出 `name``age``active``score``profile``tags` 六个新列:
185+
186+
```json
187+
{"id":1,"name":"Alice","age":30,"active":true,"score":1,"profile":{"city":"SF"},"tags":["new"]}
188+
{"id":2,"name":"Bob","age":null,"active":false,"score":1.5,"profile":{"city":"NYC"},"tags":["vip"]}
189+
```
190+
191+
如果目标表只有 `id INT`,加载后 Databend 会追加:
192+
193+
```text
194+
name VARCHAR NULL
195+
age BIGINT NULL
196+
active BOOLEAN NULL
197+
score DOUBLE NULL
198+
profile VARIANT NULL
199+
tags VARIANT NULL
200+
```
201+
202+
第二行的 `age``NULL`,不会影响 `age` 根据第一行推断为 `BIGINT``score` 同时出现整数和浮点数,因此合并为 `DOUBLE``profile``tags` 是对象和数组,因此在 Schema Evolution 中作为 `VARIANT` 列追加。
203+
204+
### 步骤 4:验证结果
205+
206+
表现在有三列,`city``score` 被自动添加:
207+
208+
```sql
209+
DESC events;
210+
```
211+
212+
```text
213+
┌─────────────────────────────────────────────────────────┐
214+
│ Field │ Type │ Null │ Default │ Extra │
215+
├───────┼──────────────┼────────┼─────────┼──────────────┤
216+
│ id │ INT │ YES │ NULL │ │
217+
│ city │ VARCHAR │ YES │ NULL │ │
218+
│ score │ BIGINT │ YES │ NULL │ │
219+
└─────────────────────────────────────────────────────────┘
220+
```
221+
222+
```sql
223+
SELECT * FROM events ORDER BY id;
224+
```
225+
226+
```text
227+
┌────────────────────────────┐
228+
│ id │ city │ score │
229+
├────┼──────┼────────────────┤
230+
│ 1 │ SF │ 9 │
231+
│ 2 │ NYC │ 8 │
232+
│ 3 │ NULL │ 7 │
233+
└────────────────────────────┘
234+
```
235+
236+
如果采样没有覆盖到后续数据中的某个字段,加载会失败并返回额外字段名。此时可以调大 `SAMPLE_FILES``SAMPLE_RECORDS_PER_FILE``SAMPLE_TOTAL_RECORDS` 后重试。
237+
108238
## 列匹配模式
109239

110240
默认按不区分大小写匹配列名。使用 `COLUMN_MATCH_MODE` 可启用大小写敏感匹配:
@@ -118,8 +248,9 @@ COLUMN_MATCH_MODE = CASE_SENSITIVE;
118248

119249
## 限制
120250

121-
- 仅支持 **Parquet** 文件。
251+
- 目前支持 **Parquet****NDJSON** 文件。
122252
- 新列追加到表末尾,始终为可空类型。
123253
- 如果同名列在多个文件中具有**不同数据类型**,加载将失败。
124254
- 不会自动提升类型(如 `INT``BIGINT`)。
125255
- 不支持通过 Schema Evolution 删除或重命名列。
256+
- NDJSON 依赖采样推断 schema;如果采样未覆盖所有字段,需要调大 `SCHEMA_EVOLUTION` 采样参数。

docs/cn/sql-reference/10-sql-commands/10-dml/dml-copy-into-table.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ copyOptions ::=
173173
[ MAX_FILES = <num> ]
174174
[ RETURN_FAILED_ONLY = <bool> ]
175175
[ COLUMN_MATCH_MODE = { case-sensitive | case-insensitive } ]
176+
[ SCHEMA_EVOLUTION = (
177+
[ SAMPLE_FILES = AUTO | <positive_integer> ]
178+
[ , SAMPLE_RECORDS_PER_FILE = AUTO | <positive_integer> ]
179+
[ , SAMPLE_TOTAL_RECORDS = AUTO | <positive_integer> ]
180+
) ]
176181

177182
```
178183

@@ -276,6 +281,21 @@ copyOptions ::=
276281
| MAX_FILES | 最大加载文件数(上限 15,000) | - |
277282
| RETURN_FAILED_ONLY | 仅返回失败的文件 | `false` |
278283
| COLUMN_MATCH_MODE | Parquet 列名匹配模式 | `case-insensitive` |
284+
| SCHEMA_EVOLUTION | NDJSON 专用:用于推断目标表中缺失列的采样选项。要求目标表启用 `ENABLE_SCHEMA_EVOLUTION = true`,并且执行角色拥有目标表的 `ALTER` 权限。 | `AUTO` 采样 |
285+
286+
### SCHEMA_EVOLUTION 选项
287+
288+
`SCHEMA_EVOLUTION` 控制 Databend 在加载前如何采样 staged NDJSON 文件。目标表启用 `ENABLE_SCHEMA_EVOLUTION = true` 后,可与 `FILE_FORMAT = (TYPE = NDJSON ...)` 搭配使用。
289+
290+
当从 Stage 或外部位置加载并触发 Schema Evolution 推断时,执行 `COPY INTO <table>` 的角色必须拥有目标表的 `INSERT``ALTER` 权限。基于查询的 COPY(例如 `COPY INTO <table> FROM (SELECT ... FROM @stage)`)仍使用原有权限要求。
291+
292+
| 选项 | 描述 | 取值 |
293+
|--------|-------------|--------|
294+
| SAMPLE_FILES | 采样的 staged 文件数量。 | `AUTO` 或正整数 |
295+
| SAMPLE_RECORDS_PER_FILE | 每个采样文件中最多采样的记录数。 | `AUTO` 或正整数 |
296+
| SAMPLE_TOTAL_RECORDS | 所有采样文件中最多采样的记录总数。 | `AUTO` 或正整数 |
297+
298+
如果省略 `SCHEMA_EVOLUTION`,Databend 会对三个采样选项都使用 `AUTO`。当前 `AUTO` 行为最多采样 64 个文件、每个文件 1,000 条记录、总计 10,000 条记录。这些内部默认值未来版本可能会调整。如果加载结果对采样策略敏感,建议显式设置 `SAMPLE_FILES``SAMPLE_RECORDS_PER_FILE``SAMPLE_TOTAL_RECORDS`。如果采样遗漏了加载过程中出现的列,COPY 会失败并返回额外列名,你可以增大采样参数后重试。
279299

280300
:::tip
281301
导入大量数据(如日志)时,建议将 `PURGE``FORCE` 均设为 `true`,可高效导入数据且无需与 Meta 服务器交互(更新已复制文件集)。但请注意,这可能导致重复数据导入。
@@ -709,7 +729,7 @@ SELECT * FROM t2;
709729

710730
### 示例 8:使用 Schema Evolution 加载
711731

712-
当加载的 Parquet 文件结构包含目标表中不存在的列时,可以使用 Schema Evolution 自动添加缺失的列。首先,在表上启用 Schema Evolution:
732+
当加载的 Parquet 或 NDJSON 文件结构包含目标表中不存在的列时,可以使用 Schema Evolution 自动添加缺失的列。对于从 Stage 或外部位置加载并触发 Schema Evolution 推断的场景,请确保执行加载的角色拥有目标表的 `INSERT``ALTER` 权限。首先,在表上启用 Schema Evolution:
713733

714734
```sql
715735
CREATE OR REPLACE TABLE invoices(order_id INT);
@@ -718,6 +738,8 @@ CREATE OR REPLACE TABLE invoices(order_id INT);
718738
ALTER TABLE invoices SET OPTIONS(ENABLE_SCHEMA_EVOLUTION = true);
719739
```
720740

741+
#### Parquet
742+
721743
然后加载具有不同结构的 Parquet 文件。Databend 会自动添加新列,缺失的值用 `NULL` 填充:
722744

723745
```sql
@@ -727,4 +749,26 @@ COPY INTO invoices
727749
FILE_FORMAT = (TYPE = PARQUET MISSING_FIELD_AS = FIELD_DEFAULT);
728750
```
729751

752+
#### NDJSON
753+
754+
对于 NDJSON,`COPY INTO` 会使用默认采样值推断缺失列。仅当需要覆盖 Databend 对 staged 文件的采样方式时,才需要添加 `SCHEMA_EVOLUTION`
755+
756+
```sql
757+
CREATE OR REPLACE TABLE events(id INT);
758+
ALTER TABLE events SET OPTIONS(ENABLE_SCHEMA_EVOLUTION = true);
759+
760+
-- 假设 @events_stage 中包含如下 NDJSON 记录:
761+
-- {"id":1,"city":"SF","score":9}
762+
COPY INTO events
763+
FROM @events_stage/
764+
FILE_FORMAT = (TYPE = NDJSON MISSING_FIELD_AS = FIELD_DEFAULT)
765+
SCHEMA_EVOLUTION = (
766+
SAMPLE_FILES = AUTO,
767+
SAMPLE_RECORDS_PER_FILE = AUTO,
768+
SAMPLE_TOTAL_RECORDS = AUTO
769+
);
770+
```
771+
772+
Databend 会采样 staged NDJSON 文件,将推断出的 `city``score` 等字段追加为可空列,然后加载数据。
773+
730774
更多详细信息,请参阅 [Schema Evolution](/guides/load-data/transform/schema-evolution)

0 commit comments

Comments
 (0)