4. 批量加载实战
当数据文件已经通过 PUT 命令上传到 Snowflake 内部阶段后,真正把数据“搬进”目标表的关键一步就是 COPY INTO。它负责读取阶段中的文件、按指定文件格式解析、按加载选项处理异常,并把结果写入目标表。本节围绕一条完整的加载链路展开:定义文件格式、执行 COPY INTO、处理错误、验证结果、排查常见问题。
一、先定义文件格式,再谈加载
COPY INTO 不会凭空猜测文件结构,必须通过 FILE_FORMAT 告诉它如何解析。推荐做法是先创建命名文件格式对象,便于复用与统一管理:
CREATE OR REPLACE FILE FORMAT ff_csv_std
TYPE = CSV
FIELD_DELIMITER = ','
RECORD_DELIMITER = '\n'
SKIP_HEADER = 1
FIELD_OPTIONALLY_ENCLOSED_BY = '"'
NULL_IF = ('NULL','','\\N')
EMPTY_FIELD_AS_NULL = TRUE
DATE_FORMAT = 'YYYY-MM-DD'
TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS'
ERROR_ON_COLUMN_COUNT_MISMATCH = TRUE;
几个参数值得注意:SKIP_HEADER=1 跳过表头;NULL_IF 把空串、NULL、\N 统一识别为 NULL;ERROR_ON_COLUMN_COUNT_MISMATCH=TRUE 让列数不匹配的文件直接报错,避免脏数据静默进入。若源文件是 JSON,则改用 TYPE=JSON,并配合 STRIP_OUTER_ARRAY=TRUE 处理顶层数组。
二、执行 COPY INTO 并理解加载选项
最基础的加载语句如下:
COPY INTO sales.orders
FROM @sales_stage/orders/
FILE_FORMAT = (FORMAT_NAME = ff_csv_std)
PATTERN = '.*orders_2024.*[.]csv'
ON_ERROR = 'ABORT_STATEMENT'
PURGE = FALSE
FORCE = FALSE;
关键选项的作用:PATTERN 用正则筛选文件,避免误加载;ON_ERROR 决定遇到坏行时的行为,可选 CONTINUE、SKIP_FILE、ABORT_STATEMENT;PURGE=TRUE 会在加载成功后删除阶段中的文件;FORCE=TRUE 会忽略“已加载过”的元数据记录,强制重新加载。默认情况下,COPY INTO 会记录每个文件的加载状态,同一文件不会被重复加载,这是它区别于普通 INSERT 的重要特性。
三、错误处理:让坏数据可控
批量加载最怕的是“一条坏行拖垮整个文件”。Snowflake 提供了两种思路:一是用 ON_ERROR 控制整体行为,二是用 VALIDATION_MODE 先做“干跑”检查。例如,在正式加载前先验证:
COPY INTO sales.orders
FROM @sales_stage/orders/
FILE_FORMAT = (FORMAT_NAME = ff_csv_std)
PATTERN = '.*orders_2024.*[.]csv'
VALIDATION_MODE = 'RETURN_ERRORS';
该语句不会真正写入数据,而是返回每个文件的错误行、错误原因和列信息。常见错误包括:列数不匹配、日期格式无法解析、数值字段出现非数字字符、字段超出目标列长度。若确认只有少量坏行,可改用 ON_ERROR='CONTINUE' 跳过坏行并继续加载,同时通过 COPY_HISTORY 查看被跳过的行数;若数据质量要求严格,则保持 ABORT_STATEMENT,让问题文件整体失败,避免污染目标表。
四、验证加载结果
加载完成后,不能只看“语句执行成功”。建议按以下顺序验证:
- 查询目标表总行数,与源文件预期行数对比:
SELECT COUNT(*) FROM sales.orders; - 检查最近一次加载状态:
SELECT * FROM TABLE(INFORMATION_SCHEMA.COPY_HISTORY('sales.orders', START_TIME=>DATEADD('hour',-1,CURRENT_TIMESTAMP()))); - 查看
ROW_COUNT、ERROR_COUNT、STATUS字段,确认是LOADED还是PARTIALLY_LOADED。 - 抽样比对关键字段,例如金额、日期、订单号是否与源文件一致。
如果发现行数偏少,优先检查 PATTERN 是否漏掉了文件、SKIP_HEADER 是否误跳了数据行、NULL_IF 是否把有效值当成了 NULL。如果行数偏多,则要确认是否重复执行了 FORCE=TRUE,或阶段中存在重复文件。
五、常见问题排查清单
| 现象 | 可能原因 | 排查动作 |
|---|---|---|
| 报“列数不匹配” | 源文件分隔符或引号不一致 | 检查 FIELD_DELIMITER、FIELD_OPTIONALLY_ENCLOSED_BY |
| 日期加载为 NULL | DATE_FORMAT 与源格式不符 | 用 VALIDATION_MODE 查看错误行 |
| 文件被跳过 | 该文件已被加载过 | 查询 COPY_HISTORY 或使用 FORCE=TRUE |
| 加载后阶段文件仍在 | PURGE 默认为 FALSE | 确认是否需要设置 PURGE=TRUE |
| 部分行丢失 | ON_ERROR=CONTINUE 跳过了坏行 | 查看 ERROR_COUNT 与错误明细 |