当MySQL执行存储过程遇到错误时,适当处理它,如继续执行或退出当前代码段,并返回有意义的错误提示是很重要的。一方面提高程序的容错能力,另一方便当程序出错时,开发人员也能准确定位错误的地方。
在本章MySQL教程中,我们将学习如何在存储过程中处理程序错误。
声明处理程序
MySQL为我们提供了一种简单的方法定义处理程序,我们可以使用DECLARE HANDLER语句,如下所示:
1
|
DECLARE
action
HANDLER
FOR
condition_value statement;
|
如果其值与condition_value匹配,MySQL将执行statement,并且根据action值选择继续或退出当前代码块。
action 可以是以下两种:
CONTINUE:继续执行当前代码块
EXIT:退出当前代码块
condition_value 是一类特定的条件,可以使一下几种:
一个MYSQL错误代码
一个标准的SQLSTATE值,如SQLWARNING,NOTFOUND ,SQLEXCEPTION等
statement 是一个语句块,从BEGIN开始,到END结束。它可以是一个简单的sql语句,也可以是很复杂的逻辑语句。
MySQL错误处理的例子
例1:当程序发生错误,将has_error值置为1。如:
1
|
DECLARE
CONTINUE
HANDLER
FOR
SQLEXCEPTION
SET
has_error = 1;
|
例2:当遇到错误时,程序将回滚之前的操作,同时给出错误提示,然后退出当前程序块。如:
1
2
3
4
5
|
DECLARE
EXIT HANDLER
FOR
SQLEXCEPTION
BEGIN
ROLLBACK
;
Select
'发生错误,执行将被回滚,程序将终止执行'
;
END
;
|
例3:对于游标或select into操作,如果出现找不到记录的情况,将no_row_found赋值为1。如:
1
|
DECLARE
CONTINUE
HANDLER
FOR
NOT
FOUND
SET
no_row_found = 1;
|
例4:如果出现重复键值,MySQL会给出1062的错误,将给出错误提示,程序继续运行。如:
1
2
|
DECLARE
CONTINUE
HANDLER
FOR
1062
Select
'错误:重复键值'
;
|
在存储过程中MySQL的处理程序的例子
首先我们创建一张数据表,为测试所用。
1
2
3
4
5
|
Create
TABLE
article_tags(
article_id
INT
,
tag_id
INT
,
PRIMARY
KEY
(article_id,tag_id)
);
|
article_tags表用来存储文章及标签之间的关系(多对多)。article_id存储文章ID,tag_id存储标签ID
其次,我们创建一个存储过程,实现插入文章ID和标签ID。
1
2
3
4
5
6
7
8
9
10
11
|
DELIMITER $$
Create
PROCEDURE
insert_article_tags(
IN
article_id
INT
,
IN
tag_id
INT
)
BEGIN
DECLARE
CONTINUE
HANDLER
FOR
1062
Select
CONCAT(
'错误:重复键值 ('
,article_id,
','
,tag_id,
')'
)
AS
msg;
-- 插入新的记录
Insert
INTO
article_tags(article_id,tag_id)
VALUES
(article_id,tag_id);
-- 返回标签数量
Select
COUNT
(*)
FROM
article_tags;
END
|
第三,利用存储过程往article_tags表添加一些数据
1
2
3
|
CALL insert_article_tags(1,1);
CALL insert_article_tags(1,2);
CALL insert_article_tags(1,3);
|
第四,这一步,我们添加一条已经存在的数据,看看上面定义的错误处理会不会执行。
1
|
CALL insert_article_tags(1,3);
|
执行这条语句后,我们会看到如下的错误提示。但是,因为在错误处理程序中我们设置了CONTINUE,因此程序还会继续执行,所以最后我们还会看到标签的数量。如下图:
如果我们将CONTINUE改为EXIT,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
DELIMITER $$
Create
PROCEDURE
insert_article_tags_2(
IN
article_id
INT
,
IN
tag_id
INT
)
BEGIN
DECLARE
EXIT HANDLER
FOR
SQLEXCEPTION
Select
'SQLException异常调用'
AS
msg;
DECLARE
EXIT HANDLER
FOR
1062
Select
'MySQL错误代码:1062'
AS
msg;
DECLARE
EXIT HANDLER
FOR
SQLSTATE
'23000'
Select
'SQLSTATE:23000'
AS
msg;
-- 插入数据
Insert
INTO
article_tags(article_id,tag_id)
VALUES
(article_id,tag_id);
-- 返回记录数
Select
COUNT
(*)
FROM
article_tags;
END
|
现在,我们再次插入一条已存在的数据:
1
|
CALL insert_article_tags_2(1,3);
|
这时,我们将智能看到错误信息,而看不到返回的记录数。
MySQL处理程序的优先级
在实际开发过程中,肯定会有多个错误处理程序,此时MySQL就需要根据优先级来调用处理。
在MySQL中,每一个error错误都会对应一个错误代码,一个SQLSTATE状态可以映射到MySQL多个错误代码,不太具体,而像SQLEXCPETION或SQLWARNING是一类SQLSTATES值,是通用的。因此,在处理优先级上,error错误首先处理,其次是SQLSTATE,最后是SQLEXCEPTION。
下面我们看一个包含这三种错误的存储过程:
1
2
3
4
5
6
7
8
9
10
11
12
|
ELIMITER $$
Create
PROCEDURE
insert_article_tags_3(
IN
article_id
INT
,
IN
tag_id
INT
)
BEGIN
DECLARE
EXIT HANDLER
FOR
1062
Select
'键值重复'
;
DECLARE
EXIT HANDLER
FOR
SQLEXCEPTION
Select
'SQLException异常'
;
DECLARE
EXIT HANDLER
FOR
SQLSTATE
'23000'
Select
'SQLSTATE 23000'
;
-- 插入记录
Insert
INTO
article_tags(article_id,tag_id)
VALUES
(article_id,tag_id);
-- 返回记录数
Select
COUNT
(*)
FROM
article_tags;
END
|
现在,我们插入一条已经存在的记录:
1
|
CALL insert_article_tags_3(1,3);
|
按照上面介绍的处理顺序,此时应该提示“键值重复”。如下图:
命名错误
首先我们看一个例子:
1
2
|
DECLARE
EXIT HANDLER
FOR
1051
Select
'请先创建数据表 abc'
;
Select
*
FROM
abc;
|
当我们查询一张不存在的的表时,提示相关错误信息,这里我们用到了1051代码。想想一下,MySQL中有相当多的错误代码,难道我们需要记住每一个吗?
当然不是,MySQL为我们提供了DECLARE CONDITION语句,用于命名错误条件,如:
1
|
DECLARE
condition_name CONDITION
FOR
condition_value;
|
condition_value是一个错误代码,如1015或SQLSTATE值。
定义之后,我们就可以使用condition_name来代替condition_value。现在,我们将上面的脚本重写,如:
1
2
3
|
DECLARE
table_not_found CONDITION
for
1051;
DECLARE
EXIT HANDLER
FOR
table_not_found
Select
'请先创建数据表 abc'
;
Select
*
FROM
abc;
|
很显然,这段代码可读性比上面的要好。
原文(英文)地址:http://www.mysqltutorial.org/mysql-error-handling-in-stored-procedures/
http://www.uncletoo.com/html/mysql/953.html