mysql存储过程需显式声明continue handler捕获sql异常,用get diagnostics提取错误信息(如mysql_errno、message_text),再写入日志表;否则异常直接退出且无记录。

MySQL存储过程本身不能自动捕获并记录异常——必须显式声明HANDLER,手动调用GET DIAGNOSTICS提取错误信息,并写入日志表;否则异常一发生就退出,不留痕迹。
用DECLARE CONTINUE HANDLER FOR SQLEXCEPTION捕获所有SQL级异常
MySQL不支持逐行try-catch,只能在存储过程BEGIN...END块内声明一个全局异常处理器。使用CONTINUE类型才能在出错后继续执行日志写入逻辑,EXIT会直接终止整个过程(除非你真想立刻退出)。
-
CONTINUE是必须的:否则INSERT INTO log_table根本不会执行 - 一个
BEGIN...END块里只能有一个SQLEXCEPTION处理器,重复声明会报错 - 它只捕获“MySQL内部抛出的错误”,比如主键冲突、除零、字段超长,但不捕获业务逻辑错误(如
IF balance 这种判断失败)
用GET DIAGNOSTICS CONDITION 1拿到错误码和错误消息
仅声明HANDLER不够,它不会自动提供错误详情。GET DIAGNOSTICS是唯一标准方式,必须紧跟在HANDLER的BEGIN块内调用,且只能取最近一次触发的异常(CONDITION 1)。
- 常用提取字段:
RETURNED_SQLSTATE(5位SQLSTATE码,如'23000')、MYSQL_ERRNO(MySQL错误号,如1062)、MESSAGE_TEXT(人类可读错误信息) - 必须先
DECLARE变量接收这些值,例如:DECLARE code CHAR(5) DEFAULT '00000'; DECLARE msg TEXT; - 如果没调用
GET DIAGNOSTICS,code和msg就是NULL,日志表里只会存空值
把异常写进日志表前,先确认表结构和权限
日志表不是可选配件,而是必需基础设施。没有它,HANDLER捕获到的错误就彻底丢失。
- 表至少要包含:
error_procedure_name(来源过程名)、error_code(MYSQL_ERRNO或RETURNED_SQLSTATE)、error_message(MESSAGE_TEXT)、error_timestamp(用CURRENT_TIMESTAMP) - 存储过程中插入日志时,别漏掉
INSERT语句的事务隔离问题:如果外层事务已ROLLBACK,日志也可能被回滚掉——建议日志表用ENGINE=MyISAM或在HANDLER里显式START TRANSACTION再COMMIT - 确保执行存储过程的数据库用户对日志表有
INSERT权限,否则HANDLER自身会因权限错误再次触发异常,形成死循环
别忽略SIGNAL和外部异常的边界
HANDLER FOR SQLEXCEPTION捕不到你主动SIGNAL抛出的自定义异常,除非你额外加一条HANDLER FOR SQLSTATE 'HY000'之类的具体码。
- 业务校验失败(如余额不足)应优先用
IF ... THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient balance';,再配对应HANDLER - 同一个
HANDLER块里不要混用ROLLBACK和日志写入——若外层无事务,ROLLBACK会报ERROR 1370 (42000),导致日志也写不进去 - 调试时可在
HANDLER里加SELECT语句输出code和msg,但上线前必须删掉,否则破坏调用方结果集结构
最常被跳过的一步是:没验证GET DIAGNOSTICS是否真拿到了值。很多日志表里满屏NULL,不是没异常,而是提取逻辑漏写了或顺序错了。











