DECLARE HANDLER必须在变量和游标声明之后、任何可执行语句之前,否则MySQL静默忽略或报ERROR 1337;顺序错误、类型不匹配(如NOT FOUND误用于主键冲突)或未显式事务控制均导致handler失效。

DECLARE HANDLER 必须在变量和游标之后、可执行语句之前声明
MySQL 对 DECLARE HANDLER 的位置有硬性约束:它不能出现在 BEGIN 块开头,也不能放在任何 SET、INSERT 等执行语句之后。否则会报错 ERROR 1337 (42000): Variable or condition declaration after cursor or handler declaration。
正确顺序只能是:
- 变量声明(
DECLARE var_name type DEFAULT ...) - 游标声明(
DECLARE cursor_name CURSOR FOR ...) - 条件声明(
DECLARE condition_name CONDITION FOR ...,可选) - Handler 声明(
DECLARE CONTINUE/EXIT HANDLER FOR ...) - 可执行语句(
START TRANSACTION、INSERT等)
漏掉这个顺序,哪怕语法全对,也会直接编译失败。
用 MySQL 错误码比 SQLSTATE 更精准,但要注意优先级
MySQL 在匹配 condition_value 时,优先级是:MySQL error code > SQLSTATE > 命名条件。这意味着如果你同时为错误码 1062(重复键)和 SQLSTATE '23000' 声明了 handler,只有 1062 的 handler 会被触发。
常见需捕获的错误码及写法:
- 主键/唯一键冲突:
DECLARE CONTINUE HANDLER FOR 1062 SET duplicate_key = 1; - 外键约束失败:
DECLARE EXIT HANDLER FOR 1452 BEGIN ROLLBACK; SELECT 'Foreign key violation'; END; - 数据截断警告(非错误,属 SQLWARNING):
DECLARE CONTINUE HANDLER FOR SQLWARNING SET warning_occurred = 1; - 空结果集(常用于游标):
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = 1;
别依赖 SQLEXCEPTION 捕获一切——它不包含 SQLWARNING 和 NOT FOUND,而这两类在业务中同样关键。
CONTINUE 和 EXIT 的行为差异直接影响事务控制逻辑
CONTINUE 表示 handler 执行完后继续运行 handler 后面的下一条语句;EXIT 则直接跳出当前 BEGIN...END 块。这对事务管理很关键:
- 用
CONTINUE时,你必须手动检查标志位(如exit_flag),再决定是否ROLLBACK或COMMIT - 用
EXIT时,handler 内必须完成所有清理(如ROLLBACK),否则事务可能处于打开状态,后续语句在隐式提交前仍属该事务 - 嵌套
BEGIN...END块中,EXIT只退出最近一层,不是整个存储过程
典型陷阱:声明 EXIT HANDLER FOR SQLEXCEPTION,但在 handler 里只 SELECT 错误信息,忘了 ROLLBACK,结果事务卡住,下次调用可能报 ERROR 1305 (42000): SAVEPOINT does not exist。
错误码 1062 处理要区分「预期重复」和「意外冲突」
不是所有 1062 都该终止流程。比如幂等插入场景,你可能想忽略重复键错误并继续:
DECLARE CONTINUE HANDLER FOR 1062
BEGIN
-- 记录日志但不中断
INSERT INTO log_table VALUES ('duplicate ignored', NOW());
END;但如果是关键约束(如用户注册时邮箱唯一),则应立即终止并回滚:
DECLARE EXIT HANDLER FOR 1062 BEGIN ROLLBACK; SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Email already registered'; END;
注意:SIGNAL 抛出的错误会传给调用方,客户端能捕获;而仅设标志位或 SELECT 不会中断上层执行流。是否向外暴露错误,取决于你的服务契约。
最易被忽略的一点:MySQL 错误码是引擎层返回的整数,不随版本变化,但某些错误(如分区表相关)在不同版本中可能映射到不同码。上线前务必在目标版本上验证 handler 是否真被触发——光看文档不够,得实测 SHOW WARNINGS 或查 information_schema.INNODB_SYS_FOREIGN 等上下文。


















