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 的重要特性。

COPY INTO 内部阶段加载链路 内部阶段 @sales_stage/orders/ orders_2024_01.csv orders_2024_02.csv 文件格式 ff_csv_std CSV / SKIP_HEADER=1 NULL_IF / 日期格式 COPY INTO PATTERN 筛选 ON_ERROR 策略 PURGE / FORCE 加载元数据去重 目标表 sales.orders 列类型匹配 约束与默认值 验证与排查 VALIDATION_MODE COPY_HISTORY 行数 / 错误明细 读取文件 解析规则 写入 校验

三、错误处理:让坏数据可控

批量加载最怕的是“一条坏行拖垮整个文件”。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,让问题文件整体失败,避免污染目标表。

四、验证加载结果

加载完成后,不能只看“语句执行成功”。建议按以下顺序验证:

  1. 查询目标表总行数,与源文件预期行数对比:SELECT COUNT(*) FROM sales.orders;
  2. 检查最近一次加载状态:SELECT * FROM TABLE(INFORMATION_SCHEMA.COPY_HISTORY('sales.orders', START_TIME=>DATEADD('hour',-1,CURRENT_TIMESTAMP())));
  3. 查看 ROW_COUNT、ERROR_COUNT、STATUS 字段,确认是 LOADED 还是 PARTIALLY_LOADED。
  4. 抽样比对关键字段,例如金额、日期、订单号是否与源文件一致。

如果发现行数偏少,优先检查 PATTERN 是否漏掉了文件、SKIP_HEADER 是否误跳了数据行、NULL_IF 是否把有效值当成了 NULL。如果行数偏多,则要确认是否重复执行了 FORCE=TRUE,或阶段中存在重复文件。

五、常见问题排查清单

现象可能原因排查动作
报“列数不匹配”源文件分隔符或引号不一致检查 FIELD_DELIMITER、FIELD_OPTIONALLY_ENCLOSED_BY
日期加载为 NULLDATE_FORMAT 与源格式不符用 VALIDATION_MODE 查看错误行
文件被跳过该文件已被加载过查询 COPY_HISTORY 或使用 FORCE=TRUE
加载后阶段文件仍在PURGE 默认为 FALSE确认是否需要设置 PURGE=TRUE
部分行丢失ON_ERROR=CONTINUE 跳过了坏行查看 ERROR_COUNT 与错误明细
建议把“先 VALIDATION_MODE 验证、再正式 COPY INTO、最后 COPY_HISTORY 复核”固化为标准加载流程,可以显著降低批量加载的排障成本。