SQL Server和PostgreSQL支持结构化异常处理并可捕获DML错误后继续执行,MySQL几乎无法真正捕获DML错误,Oracle需在PL/SQL块中实现;各数据库语法与行为差异显著,需按上下文适配。

不能在所有数据库里都“捕获DML错误并继续执行”——这取决于你用的是哪种数据库,以及你把DML放在什么上下文中执行。SQL Server 和 PostgreSQL 支持结构化异常处理,能真正捕获后继续;MySQL 几乎做不到;Oracle 可以,但必须写在 PL/SQL 块里。
SQL Server 中用 TRY…CATCH 捕获 INSERT/UPDATE 后继续
在存储过程或批处理中,把 DML 包进 TRY…CATCH 块即可。关键不是“吞掉错误”,而是明确知道哪些错误可忽略、哪些必须中断。
-
ERROR_NUMBER()必须在CATCH第一行就存进变量,后续语句(比如INSERT INTO log_table)可能覆盖它 - 想继续执行后续逻辑,就别在
CATCH里用THROW或RAISERROR——否则事务会回滚或上层收到异常 - 避免在
CATCH里做复杂操作:比如调用含触发器的表、查视图、写大字段,这些可能再次出错导致整个事务失败 - 示例场景:批量插入用户,某条因邮箱重复失败,记录日志后继续插下一条
PostgreSQL 中用 BEGIN…EXCEPTION 捕获并静默跳过
PL/pgSQL 函数或 DO 块里才能用 BEGIN…EXCEPTION。注意:它默认不阻止主 DML 提交——也就是说,即使你在触发器里捕获了错误,外面的 INSERT 仍会成功,容易误以为“没出错”。
- 用
WHEN unique_violation THEN比只靠WHEN OTHERS THEN更安全,避免掩盖真实问题 -
GET STACKED DIAGNOSTICS能拿到更准的上下文,比如冲突发生在哪张表、哪个约束,比SQLERRM更可靠 - 日志表字段建议包含:
trigger_name、operation('INSERT'/'UPDATE')、error_code、error_message - 别在
EXCEPTION里发 HTTP 请求或调外部函数——拖慢主事务,且可能因超时再抛错
MySQL 无法真正捕获 DML 错误,只能绕开
MySQL 触发器和普通 SQL 脚本都不支持 TRY…CATCH。所谓“捕获”,实际是提前检查 + 主动报错 + 用 HANDLER 拦截,但仅对 SIGNAL 有效,对主键冲突、外键失败等存储引擎级错误完全无感。
- 唯一靠谱路径是改用存储过程 +
DECLARE CONTINUE HANDLER FOR 1062(对应 ERROR 1062),然后配INSERT IGNORE或ON DUPLICATE KEY UPDATE - 触发器内禁止执行任何可能引发约束冲突的 DML,尤其是对有唯一索引的表写日志——那会直接让原始语句失败
- 并发场景下,
SELECT … INTO预检 +IF判断仍可能撞上唯一约束,这不是 bug,是 MySQL 的设计限制 - 不要试图在触发器里调用带
HANDLER的存储过程——MySQL 明确禁止触发器内调用含事务或错误处理的子程序
最易被忽略的一点:所谓“继续执行”,不等于“业务逻辑不变”。比如 PostgreSQL 捕获了唯一冲突却没重抛异常,上层应用可能以为数据已写入,实际根本没进目标表——这种语义错位比报错更危险。

















