在大规模导入数据时,需要记录错误行的需求几乎在自动加载大量数据到数据库后立即出现。传统上,任何约束的违反(例如,数据类型的违反、约束条件)都会导致整个导入过程的中断,即使只是由于一行错误 — 结果是正确和不正确的记录都被丢失。
问题在于不仅要加载尽可能多的正确数据,还要留下审计和详细的错误描述,以便后续手动处理,而不是简单地“回滚所有”。
解决方案: 对于某些数据库系统(例如 PostgreSQL、支持 SSIS 的 SQL Server、带有 EXTERNAL TABLES 工具的 Oracle),存在加载的错误行分离机制。对于通用方法 — 使用临时表和通过 OUTER JOIN 和 NOT EXISTS 的后上传验证,以及手动记录问题行的“日志表”。
代码示例:
-- 1. 加载所有内容到临时 staging 表 BULK INSERT staging_payments FROM 'payments.csv' WITH (...) -- 2. 过滤正确的 INSERT INTO payments (id, amount, ...) SELECT id, amount, ... FROM staging_payments WHERE amount > 0 AND status IN ('approved', 'pending'); -- 3. 将错误行记录到单独的表中 INSERT INTO import_errors (row_data, error_desc) SELECT *, '状态不正确或负值金额' FROM staging_payments WHERE amount <= 0 OR status NOT IN ('approved', 'pending');
关键特性:
可以在 SQL 中通过 ON ERROR RESUME NEXT 实现所有内容吗?
SQL 标准不包含用于大规模操作导入的类似结构。在大多数情况下,错误报告在外部工具或日志表中生成,如上所述。
可以使用事务来回滚仅错误的行吗?
回滚事务会一起回滚所有行(原子性)。仅在首先加载到 staging 表时,才可以进行部分保存,之后手动提交成功和记录不成功的行。
为什么通过 TRY/CATCH 在循环中捕获每一条 INSERT 错误效率不高?
通过单个 INSERT + TRY/CATCH 顺序处理数百万行,导致与批量或 BULK INSERT + 之后检查相比表现出灾难性的性能下降。
公司通过 INSERT 脚本加载数万行,但由于唯一键错误,整批数据被回滚。这导致停机和数据丢失。
优点:
使用 staging 表、错误日志记录以及将输入数据分为“有效/无效”类别,显著加快了导入,存储并自动分析了拒绝记录。
优点: