declare handler 必须声明在 begin ... end 块内、所有变量和游标之后、首个可执行语句之前,仅对同块中其后的语句生效;类型需匹配,推荐 continue handler for sqlexception 并配合手动事务控制。

MySQL 存储过程里 DECLARE HANDLER 不是“写了就生效”,它必须位置正确、类型匹配、作用域清晰,否则错误照常中断,事务不会回滚。
DECLARE HANDLER 必须放在哪儿才起作用
它只对**同一 BEGIN ... END 块内、声明之后的语句**生效,且顺序不能错:
- 必须在
BEGIN后、首个可执行语句(如INSERT、SELECT INTO)之前 - 必须在所有
DECLARE变量、游标之后,但在任何 DML 或查询之前 - 如果写在
IF分支里、或存储过程末尾,handler 完全被忽略 - 嵌套
BEGIN ... END块需各自声明 handler,外层不管内层错误
该用 CONTINUE 还是 EXIT?SQLEXCEPTION 还是具体错误码?
选错类型是 handler 失效最常见原因:
- 优先用
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION:覆盖主键冲突(SQLSTATE '23000')、列不存在、外键失败等绝大多数运行时异常 - 别用
NOT FOUND捕获INSERT或UPDATE失败——它只在游标FETCH到末尾时触发 - 避免硬写 MySQL 错误号(如
1062),改用SQLSTATE更稳定(主键冲突统一是'23000') - 用
EXIT HANDLER时,handler 执行完就跳出当前块,若没手动ROLLBACK,事务可能残留未提交状态
handler 里能安全做什么?怎么拿到真实错误信息?
handler 体受限严格,且错误上下文不会自动保留:
- 只能做原子操作:赋值变量、写日志(到已存在表或临时表)、调用简单函数;禁止
COMMIT/ROLLBACK、禁止再查业务表、禁止嵌套DECLARE -
GET DIAGNOSTICS必须是 handler 内**第一条语句**,否则失效:GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE, @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
- 不要只设
SET @err = 1就完事——没上下文,你根本不知道哪条语句、为什么错 - 日志写入建议用
INSERT INTO error_log(表必须提前建好),别在 handler 里CREATE TABLE或写入不存在的表,否则二次报错直接中断
事务回滚必须手动控制,且要防隐式提交
MySQL 从不自动回滚,handler 只是“通知你出错了”,后续动作全靠自己写:
- 开头必须显式
START TRANSACTION,不能依赖AUTOCOMMIT = 0(易被其他会话干扰) - 在 handler 中设标志位(如
SET @rollback_flag = 1),然后在事务体末尾统一判断:IF @rollback_flag = 1 THEN ROLLBACK; ELSE COMMIT; END IF;
- 避开隐式提交语句:
CREATE TABLE、ALTER TABLE、LOCK TABLES、SET AUTOCOMMIT = 1—— 它们会让前面的START TRANSACTION失效,导致ROLLBACK无效果 - 若需建表,改用
CREATE TEMPORARY TABLE,它不触发提交
最容易被忽略的是:handler 生效的前提是它真的“管得到”那条出错语句——位置、作用域、类型三者缺一不可;而事务能否真正回滚,取决于你有没有在 handler 触发后,还来得及执行 ROLLBACK,且中间没穿插任何隐式提交操作。











