sql server需用try...catch配合set xact_abort on写日志;postgresql须在函数内用exception块及get stacked diagnostics;mysql需用exit handler配合get diagnostics;跨库日志表应统一用text、timestamp(3)和db_type字段确保兼容性。

SQL Server 中用 TRY...CATCH 捕获异常并写入日志表
SQL Server 存储过程中必须用 TRY...CATCH 结构才能捕获运行时错误;RAISERROR 或 THROW 不会自动触发 CATCH 块,除非错误严重级别 ≥ 11(THROW 默认是 16,安全)。
常见错误现象:直接在 CATCH 里执行 INSERT INTO error_log 却没生效——大概率是事务未提交、日志表不存在、或 CATCH 内部又出错导致静默失败。
-
CATCH块中应优先用SET XACT_ABORT ON开头,避免因事务状态异常导致日志插入失败 - 务必检查日志表是否存在且字段类型匹配:
ERROR_NUMBER()是int,ERROR_MESSAGE()最好对应nvarchar(4000)或更长 - 不要在
CATCH中调用可能失败的复杂逻辑(如远程查询、临时表操作),专注写日志
PostgreSQL 中用 EXCEPTION 块记录 PL/pgSQL 错误
PostgreSQL 不支持 TRY...CATCH,而是用 EXCEPTION 块捕获错误;关键点在于:只有在 BEGIN ... EXCEPTION 匿名块或函数体内才生效,不能用于顶层存储过程(即 CREATE PROCEDURE 在 PG 11+ 才支持,且不支持 EXCEPTION)。
所以实际可行路径只有一条:把业务逻辑封装进 CREATE FUNCTION(返回 void),并在其中用 EXCEPTION 捕获。
- 使用
GET STACKED DIAGNOSTICS获取完整上下文,比如v_detail := pg_exception_detail - 日志表插入语句必须是简单
INSERT,避免触发新异常;建议用INSERT ... VALUES而非INSERT ... SELECT - PG 的
SQLSTATE是 5 字符码(如'23505'表示唯一约束冲突),比错误号更具可读性,建议存入日志表
MySQL 存储过程中如何可靠记录错误(5.7+)
MySQL 用 DECLARE HANDLER 处理异常,但默认不中断执行流程,容易漏掉错误;必须显式声明 EXIT HANDLER 并配合 ROLLBACK 和日志写入,否则事务继续、错误被吞。
典型陷阱:Handler 中调用另一个存储过程写日志,而该过程本身报错,导致整个 Handler 失效——MySQL 不支持嵌套异常处理。
- Handler 内只能做极简操作:赋值变量 + 单条
INSERT INTO error_log+ROLLBACK - 用
GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT提取错误信息 - MySQL 的
error_log表需禁用AUTO_INCREMENT主键冲突风险,建议用UUID()或NOW(3)辅助去重
跨数据库日志表设计要注意的兼容点
不同数据库对 NULL、字符串截断、时间精度、错误消息长度容忍度差异大,日志表字段若按某一种数据库设计,很可能在另一库中写入失败或丢数据。
例如 SQL Server 的 ERROR_MESSAGE() 可达 2048 字符,而 MySQL 的 MESSAGE_TEXT 默认只返回前 1024 字节;PostgreSQL 的 pg_exception_context 可能含换行符,直接插进 MySQL VARCHAR 会报错。
- 统一用
TEXT或LONGTEXT类型存消息体,避免VARCHAR(255)这类短字段 - 时间字段用
TIMESTAMP(带时区)或DATETIME(3),别用SMALLDATETIME(SQL Server 精度仅 1 秒) - 加一个
db_type VARCHAR(20)字段,方便后续查日志时区分来源,避免把 PG 的SQLSTATE当成 SQL Server 的ERROR_NUMBER解析
真正难的不是写一次日志,而是让日志在任意异常路径下都不丢、不卡、不污染主事务——这意味着 handler 本身必须是“无副作用”的原子操作,连事务隔离级别都要提前设为 READ COMMITTED 避免锁表。











