一、定义条件 (DECLARE CONDITION)
语法:
DECLARE condition_name CONDITION FOR condition_value; condition_value: { SQLSTATE [VALUE] 'sqlstate_value' | mysql_error_code }
示例:
方法一:使用 SQLSTATE 值(5字符字符串)
DECLARE table_not_found CONDITION FOR SQLSTATE '42S02';
方法二:使用 MySQL 错误代码(数值)
DECLARE duplicate_key CONDITION FOR 1062;
DECLARE table_not_found CONDITION FOR 1146;
二、定义处理程序 (DECLARE HANDLER)
处理程序定义了当特定错误发生时应该执行什么操作。
语法:
DECLARE handler_type HANDLER FOR condition_value [, condition_value] ... statement;
handler_type: {
CONTINUE 继续执行
EXIT 退出当前BEGIN...END块
UNDO (MySQL不支持)
}
condition_value: {
SQLSTATE [VALUE] 'sqlstate_value'
condition_name
SQLWARNING
NOT FOUND
SQLEXCEPTION
mysql_error_code
}
处理程序类型 (handler_type):
`CONTINUE`: 遇到错误后继续执行后续语句
`EXIT`: 遇到错误后立即退出当前 `BEGIN...END` 块
`UNDO`: 撤回操作(MySQL 目前不支持)
错误条件类型 (condition_value):
`SQLSTATE 'xxxxx'`: 特定的5字符SQL状态值
`condition_name`: 已定义的条件名称
`SQLWARNING`: 所有以 '01' 开头的 SQLSTATE 值(警告)
`NOT FOUND`: 所有以 '02' 开头的 SQLSTATE 值(未找到数据)
`SQLEXCEPTION`: 所有不以 '01' 或 '02' 开头的 SQLSTATE 值(真正的异常)
`mysql_error_code`: 特定的MySQL数值错误代码
三、处理程序示例
多种定义方式:
1. 捕获特定 SQLSTATE
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @info = 'DUPLICATE KEY';
2. 捕获特定错误代码
DECLARE EXIT HANDLER FOR 1062 SET @info = 'DUPLICATE ENTRY';
3. 使用预定义的条件(推荐,可读性高)
DECLARE duplicate_key_error CONDITION FOR 1062;
DECLARE EXIT HANDLER FOR duplicate_key_error
BEGIN
SET @info = '重复键错误发生';
SET @error_time = NOW();
END;
4. 捕获某一类错误
DECLARE EXIT HANDLER FOR NOT FOUND SET @info = '未找到数据';
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info = '发生异常';
四、完整示例分析
CREATE TABLE t8(s1 INT, PRIMARY KEY(s1));
DELIMITER //
CREATE PROCEDURE handlerdemo()
BEGIN
定义处理程序:遇到主键冲突(23000)时继续执行,并设置变量
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @X2 = 1;
SET @X = 1; @X = 1
INSERT INTO t8 VALUES(1); 成功插入
SET @X = 2; @X = 2
INSERT INTO t8 VALUES(1); 主键冲突,触发处理程序
SET @X = 3; 继续执行,@X = 3
END //
DELIMITER ;
CALL handlerdemo();
SELECT @X; 结果为 3
执行过程分析:
1. 第一次 INSERT 成功
2. 第二次 INSERT 因主键冲突失败,SQLSTATE = '23000'
3. 触发 CONTINUE HANDLER,执行 `SET @X2 = 1`
4. 继续执行后续的 `SET @X = 3`
5. 最终 @X 的值为 3
五、实际应用场景
场景1:优雅处理重复插入
DELIMITER //
CREATE PROCEDURE insert_user(
IN p_username VARCHAR(50),
IN p_email VARCHAR(100)
)
BEGIN
DECLARE EXIT HANDLER FOR 1062 -- 重复键错误
BEGIN
SELECT '用户已存在,插入失败' AS result;
END;
INSERT INTO users(username, email) VALUES(p_username, p_email);
SELECT '用户插入成功' AS result;
END //
DELIMITER ;
场景2:处理查询无结果的情况
DELIMITER //
CREATE PROCEDURE get_user_score(IN p_user_id INT)
BEGIN
DECLARE v_score INT;
DECLARE EXIT HANDLER FOR NOT FOUND
BEGIN
SELECT '用户不存在' AS result;
END;
SELECT score INTO v_score FROM users WHERE id = p_user_id;
SELECT CONCAT('用户分数: ', v_score) AS result;
END //
DELIMITER ;
场景3:复杂的错误日志记录
DELIMITER //
CREATE PROCEDURE safe_operation()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
获取错误信息
GET DIAGNOSTICS CONDITION 1
@err_no = MYSQL_ERRNO,
@err_msg = MESSAGE_TEXT;
记录错误日志
INSERT INTO error_logs(error_code, error_message, error_time)
VALUES(@err_no, @err_msg, NOW());
返回友好提示
SELECT '操作失败,请联系管理员' AS result;
END;
主要业务逻辑
END //
DELIMITER ;
六、最佳实践建议
1. 使用命名条件:提高代码可读性和可维护性
2. 合理选择处理类型:
使用 `CONTINUE` 处理可恢复的错误
使用 `EXIT` 处理严重错误
3. 分层处理:可以在嵌套的 `BEGIN...END` 块中定义不同的处理程序
4. 错误信息获取:使用 `GET DIAGNOSTICS` 获取详细的错误信息
5. 资源清理:在 `EXIT HANDLER` 中进行必要的资源释放和状态清理
七、获取错误信息
MySQL 5.6+ 支持更详细的错误信息获取:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
@p1 = MYSQL_ERRNO,
@p2 = MESSAGE_TEXT,
@p3 = RETURNED_SQLSTATE;
记录或处理错误信息
INSERT INTO error_logs(...) VALUES(@p1, @p2, @p3, NOW());
END;
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程