@@ -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;
3435ALTER 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
7986DESC 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 ` 采样参数。
0 commit comments