DECLARE HANDLER 必须在可能出错语句前、同一 BEGIN ... END 块内声明,优先用 CONTINUE HANDLER FOR SQLEXCEPTION;仅支持设变量、写日志等原子操作,不可 COMMIT/ROLLBACK 或查表。

MySQL 存储过程中 DECLARE HANDLER 怎么写才不被忽略
MySQL 的 DECLARE HANDLER 不是“写了就生效”,它只对后续声明的语句块(BEGIN ... END 内)起作用,且必须在出错语句之前定义。很多人把 handler 放在存储过程末尾,或者放在 IF 分支里,结果异常一抛就退出,handler 根本没机会触发。
实操建议:
-
DECLARE HANDLER必须放在存储过程体开头、所有变量/游标声明之后,但要在任何可能出错的语句(如INSERT、SELECT ... INTO)之前 - handler 作用域仅限当前
BEGIN ... END块;嵌套块需各自声明,外层 handler 不管内层错误 - 优先用
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION,比SQLWARNING或具体错误码更稳妥,覆盖大部分运行时异常 - 别依赖
GET DIAGNOSTICS拿完整错误信息——MySQL 5.7+ 才稳定支持,且必须紧跟在出错语句后执行,中间插了SET就失效
为什么 INSERT 报错后 CONTINUE HANDLER 没拦住
常见现象:执行 INSERT INTO t VALUES (1, 'a'); 因主键冲突报 ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY',但 handler 没触发,过程直接中断。根本原因是 handler 类型选错了。
实操建议:
- 用
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION,不是NOT FOUND(那是给SELECT ... INTO无结果用的) - 避免混用
EXIT HANDLER—— 它会让整个BEGIN ... END块退出,看起来像“没捕获”,其实是 handler 执行完就跳出了 - 测试时手动触发错误:比如在 handler 下面立刻写
INSERT INTO nonexistent_table VALUES(1);,确认 handler 是否真生效
DECLARE HANDLER 里能安全做哪些事
handler 里不是什么都能干。MySQL 要求 handler 体必须是“原子操作”:不能含事务控制语句(COMMIT/ROLLBACK),不能调用会改变 SQL 状态的函数(如 GET_LOCK()),也不能再声明新 handler。
实操建议:
- 只做三类事:设状态变量(
SET @err_flag = 1;)、写日志表(INSERT INTO log_table ...)、简单赋值(SET v_msg = 'dup key';) - 别在 handler 里查表——如果查的表本身出错,会二次触发 handler,导致无限递归或报
ERROR 1370 (42000): execute command denied - 需要回滚?在主逻辑里用
START TRANSACTION+DECLARE EXIT HANDLER FOR SQLEXCEPTION包住整段操作,而不是在 continue handler 里硬写ROLLBACK
MySQL 5.6 和 8.0 在 handler 行为上的关键差异
MySQL 5.6 对 handler 的错误匹配比较宽松,比如 SQLEXCEPTION 有时会漏掉某些警告升级的错误;而 8.0 更严格,但也修复了 5.6 中 handler 被隐式重置的 bug(比如在游标循环里重复声明 handler 会失效)。
实操建议:
- 跨版本兼容写法:统一用
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION, SQLWARNING,不依赖具体错误码 - 8.0 可用
GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;拿错误细节,5.6 不支持CONDITION 1,只能靠GET DIAGNOSTICS拿第一项,且字段名不同 - 生产环境别假设错误码不变——比如
ER_DUP_ENTRY在不同 MySQL 版本里可能是 1062 或 1557,用符号名不如用类别可靠
handler 的边界很窄:它不接管连接、不恢复事务、不重试操作。真正难的不是写那几行声明,而是想清楚“出错后流程该往哪走”——是跳过、补默认值、记日志、还是通知调用方。这些决策一旦定错,handler 写得再准也没用。










