mysql的declare handler只接受sqlstate五位字符串(如'23000'),不支持直接使用错误码1062;常用sqlstate包括'23000'(完整性约束)、'22003'(数值越界)、'22012'(算术异常);推荐用命名条件提升可读性,并在continue handler中用get diagnostics获取错误详情。

必须用 SQLSTATE,不能用 MySQL 错误码
MySQL 的 DECLARE HANDLER 不接受整数错误码(如 1062)作为条件值,写 DECLARE EXIT HANDLER FOR 1062 会直接报语法错误。它只认 5 位字符串格式的 SQLSTATE,比如主键冲突对应 '23000',除零对应 '22012'。虽然文档里说“也支持 MySQL error code”,但那是历史兼容性写法,且优先级高于 SQLSTATE —— 实际测试中,多数版本(尤其是 8.0+)已严格校验语法,FOR 1062 会被拒绝。
常见业务错误对应的 SQLSTATE 值
日常开发中真正需要捕获的错误其实很集中,记住这三个就覆盖 90% 场景:
-
'23000':完整性约束失败,包括主键重复(1062)、唯一索引冲突、外键不匹配(1452) -
'22003':数值越界,比如往TINYINT插256,或DECIMAL(5,2)存1000.00 -
'22012':算术异常,如除零、NULL参与加减乘除、开负数平方根
查不到具体 SQLSTATE?执行出错后看客户端返回的完整错误信息,例如:ERROR 1062 (23000): Duplicate entry 'xxx' for key 'PRIMARY',括号里就是你要用的值。
用命名条件提升可读性
硬写 '23000' 容易看错、难维护。推荐先声明命名条件:
DECLARE dup_key CONDITION FOR SQLSTATE '23000'; DECLARE num_overflow CONDITION FOR SQLSTATE '22003';
再在 handler 中引用:
DECLARE EXIT HANDLER FOR dup_key BEGIN ROLLBACK; RESIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '用户名已存在'; END;
这样逻辑清晰,也方便后续统一调整映射关系。
CONTINUE handler 里必须手动检查错误状态
如果用了 DECLARE CONTINUE HANDLER FOR SQLEXCEPTION,它不会中断执行,但也不会自动暴露错误细节。想区分是主键冲突还是数值越界,得靠 GET DIAGNOSTICS:
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
@sqlstate = RETURNED_SQLSTATE,
@errno = MYSQL_ERRNO,
@text = MESSAGE_TEXT;
IF @sqlstate = '23000' THEN
INSERT INTO error_log VALUES ('dup', @text, NOW());
ELSEIF @sqlstate = '22003' THEN
INSERT INTO error_log VALUES ('overflow', @text, NOW());
END IF;
END;
注意:GET DIAGNOSTICS 必须放在 handler 开头,否则可能读不到最新错误上下文;@errno 是 MySQL 原生错误码,仅作辅助判断,不能用于 FOR 条件。
最常被忽略的一点:MyISAM 表不支持事务,ROLLBACK 无效;DDL 语句(如 ALTER TABLE)会隐式提交,导致前面的 START TRANSACTION 失效。这些地方即使写了 handler,也拦不住“部分生效”。务必确认表引擎和语句类型再设计异常流。











