编程数据工程师

如何在 SQL 中实现错误和不成功操作的分开处理,以在大规模导入数据时不丢失问题行的信息?

用 Hintsage AI 助手通过面试

回答。

在大规模导入数据时,需要记录错误行的需求几乎在自动加载大量数据到数据库后立即出现。传统上,任何约束的违反(例如,数据类型的违反、约束条件)都会导致整个导入过程的中断,即使只是由于一行错误 — 结果是正确和不正确的记录都被丢失。

问题在于不仅要加载尽可能多的正确数据,还要留下审计和详细的错误描述,以便后续手动处理,而不是简单地“回滚所有”。

解决方案: 对于某些数据库系统(例如 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 + 之后检查相比表现出灾难性的性能下降。

典型错误和反模式

  • 试图在未经验证或没有 staging 表的情况下进行数据的大规模加载,会导致在出现任何错误时整个加载回滚。
  • 在循环中启动多个单独的 INSERT 并捕获错误 — 会大大减慢加载,负面影响锁定。
  • 仅将错误记录到外部文件而不是表中,会使审计和纠正自动化变得复杂。

生活中的示例

负面案例

公司通过 INSERT 脚本加载数万行,但由于唯一键错误,整批数据被回滚。这导致停机和数据丢失。

优点:

  • 安全性(没有不正确的数据)。 缺点:
  • 浪费时间,无法部分保存,需手动修复整个文件。

正面案例

使用 staging 表、错误日志记录以及将输入数据分为“有效/无效”类别,显著加快了导入,存储并自动分析了拒绝记录。

优点:

  • 高性能,能够进行前处理和后处理,错误报告透明。 缺点:
  • 需要日志表并进行额外查询以支持该过程。