<blockquote><p><strong>在存储过程或函数中,一定要主要异常处理的位置。通常放在DECLARE的最后,若存储过程有返回值,可通过类似如下方式解决:
</strong></p><p><strong>DECLARE EXIT HANDLER FOR SQLEXCEPTION </strong></p><p><strong>BEGIN</strong></p><p><strong>.....</strong></p><p><strong>SET @info='ERROR';</strong></p><p><strong>END;</strong></p></blockquote><p>在MySQL中,特定异常需要特定处理。这些异常可以联系到错误,以及子程序中的一般流程控制。定义异常是事先定义程序执行过程中遇到的问题,异常处理定义了在遇到问题时对应当采取的处理方式,并且保证存储过程或者函数在遇到错误时或者警告时能够继续执行。</p><h1>1 异常定义</h1><p><strong>1.1 语法</strong></p><p>DECLARE condition_name CONDITION FOR [condition_type];</p><p><strong>1.2 说明</strong></p><p><strong>condition_name</strong>参数表示异常的名称;</p><p><strong>condition_type</strong>参数表示条件的类型,condition_type由SQLSTATE [VALUE] sqlstate_value|<a>mysql</a>_error_code组成:</p><p>sqlstate_value和mysql_error_code都可以表示MySQL的错误;sqlstate_value为长度为5的字符串类型的错误代码;mysql_error_code为数值类型错误代码;</p><p><strong>1.3 示例</strong></p><p>定义“ERROR 1148(42000)”错误,名称为command_not_allowed。可以有以下两种方法:</p><p><strong>//</strong><strong>方法一:使用sqlstate_value</strong></p><p>DECLARE command_not_allowed CONDITION FOR SQLSTATE '42000';</p><p><strong>//</strong><strong>方法二:使用mysql_error_code</strong></p><p>DECLARE command_not_allowed CONDITION FOR 1148;</p><h1>2 自定义异常处理</h1><p><strong>2.1 异常处理语法</strong></p><p>DECLARE handler_type HANDLER FOR condition_value [,...] sp_statement</p><p><strong>2.2 参数说明</strong></p><p><strong>handler_type:</strong> CONTINUE|EXIT|UNDO</p><p>handler_type为错误处理方式,参数为3个值之一;CONTINUE表示遇到错误不处理,继续执行;EXIT表示遇到错误时马上退出;UNDO表示遇到错误后撤回之前的操作,MySQL暂不支持回滚操作;</p><p><strong>condition_value:</strong> SQLSTATE [VALUE] sqlstate_value| condition_name|SQLWARNING|NOT FOUND|SQLEXCEPTION|mysql_error_code</p><p>condition_value表示错误类型;SQLSTATE [VALUE] sqlstate_value为包含5个字符的字符串错误值;
condition_name表示DECLARE CONDITION定义的错误条件名称;SQLWARNING匹配所有以01开头的SQLSTATE错误代码;NOT FOUND匹配所有以02开头的SQLSTATE错误代码;SQLEXCEPTION匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码;mysql_error_code匹配数值类型错误代码;</p><p><strong>2.3 异常捕获方法</strong></p><p>//方法一:捕获sqlstate_value异常</p><p>//这种方法是捕获sqlstate_value值。如果遇到sqlstate_value值为"42S02",执行CONTINUE操作,并输出"NO_SUCH_TABLE"信息</p><p>DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @info='NO_SUCH_TABLE';</p><p>//方法二:捕获mysql_error_code异常</p><p>//这种方法是捕获mysql_error_code值。如果遇到mysql_error_code值为1146,执行CONTINUE操作,并输出"NO_SUCH_TABLE"信息;</p><p>DECLARE CONTINUE HANDLER FOR 1146 SET @info='NO_SUCH_TABLE';</p><p>//方法三:先定义条件,然后捕获异常</p><p>DECLARE no_such_table CONDITION FOR 1146;</p><p>DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SET @info='NO_SUCH_TABLE';</p><p>//方法四:使用SQLWARNING捕获异常</p><p>DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';</p><p>//方法五:使用NOT FOUND捕获异常</p><p>DECLARE EXIT HANDLER FOR NOT FOUND SET @info='NO_SUCH_TABLE';</p><p>//方法六:使用SQLEXCEPTION捕获异常</p><p>DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR';</p><h1>3 综合示例</h1><p><strong>创建一个表,设置该表的主键,在不定义异常处理和定义异常处理情况下看执行到哪一步。</strong></p><p><strong>show databases;</strong></p><p><strong>use wms;</strong></p><p><strong>create</strong> <strong>table</strong> <strong>location</strong></p><p><strong>(</strong></p><p><strong>location_id int</strong> <strong>primary</strong> <strong>key,</strong></p><p><strong>location_name varchar(50)</strong></p><p><strong>);</strong></p><p><strong>示例1:不定义异常情况下</strong></p><p><strong>DELIMITER //</strong></p><p><strong>CREATE</strong> <strong>PROCEDURE</strong> <strong>handlerInsertNoException()</strong></p><p><strong>BEGIN</strong></p><p><strong> /DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2=1;/</strong></p><p><strong> SET</strong> <strong>@x=1;</strong></p><p><strong> INSERT</strong> <strong>INTO</strong> <strong>location VALUES</strong> <strong>(1,'Beijing');</strong></p><p><strong> SET</strong> <strong>@x=2;</strong></p><p><strong> INSERT</strong> <strong>INTO</strong> <strong>location VALUES</strong> <strong>(1,'Wuxi');</strong></p><p><strong> SET</strong> <strong>@x=3;</strong></p><p><strong>END;</strong></p><p><strong>//</strong></p><p><strong>DELIMITER ;</strong></p><p>调用存储过程与结果:</p><p><strong>mysql> call handlerInsertNoException();</strong></p><p><strong>ERROR 1062 (23000): Duplicate entry '1'</strong> <strong>for</strong> <strong>key</strong> <strong>'PRIMARY'</strong></p><p><strong>mysql> select</strong> <strong>@x;</strong></p><p><strong>+------+</strong></p><p><strong>| @x |</strong></p><p><strong>+------+</strong></p><p><strong>| 2 |</strong></p><p><strong>+------+</strong></p><p><strong>1 row in</strong> <strong>set</strong> <strong>(0.00 sec)</strong></p><p> </p><p><strong>mysql> select</strong> <strong>* from</strong> <strong>location;</strong></p><p><strong>+-------------+---------------+</strong></p><p><strong>| location_id | location_name |</strong></p><p><strong>+-------------+---------------+</strong></p><p><strong>| 1 | Beijing |</strong></p><p><strong>+-------------+---------------+</strong></p><p><strong>1 row in</strong> <strong>set</strong> <strong>(0.00 sec)</strong></p><p>注意:操作示例2前要清空表中数据,并退出重新登录,以免客户端变量@x影响,详细说明参见结论中的第一点。</p><p><strong>mysql> truncate</strong> <strong>table</strong> <strong>location;</strong></p><p><strong>Query OK, 0 rows</strong> <strong>affected (0.04 sec)</strong></p><p><strong>mysql> select</strong> <strong>* from</strong> <strong>location;</strong></p><p><strong>Empty set</strong> <strong>(0.00 sec)</strong></p><p><strong>mysql> exit;</strong></p><p><strong>Bye</strong></p><p> </p><p><strong>david@Louis:~$ mysql -u root -p</strong></p><p><strong>Enter password:</strong></p><p><strong>Welcome to</strong> <strong>the MySQL monitor. Commands end</strong> <strong>with</strong> <strong>; or</strong> <strong>\g.</strong></p><p><strong>Your MySQL connection</strong> <strong>id is</strong> <strong>53</strong></p><p><strong>Server version: 5.5.38-0ubuntu0.14.04.1 (Ubuntu)</strong></p><p> </p><p><strong>mysql> use wms;</strong></p><p><strong>Reading table</strong> <strong>information for</strong> <strong>completion of</strong> <strong>table</strong> <strong>and</strong> <strong>column</strong> <strong>names</strong></p><p><strong>You can turn off</strong> <strong>this feature to</strong> <strong>get a quicker startup with</strong> <strong>-A</strong></p><p> </p><p><strong>Database</strong> <strong>changed</strong></p><p><strong>mysql> select</strong> <strong>* from</strong> <strong>location;</strong></p><p><strong>Empty set</strong> <strong>(0.00 sec)</strong></p><p> </p><p><strong>mysql> select</strong> <strong>@x;</strong></p><p><strong>+------+</strong></p><p><strong>| @x |</strong></p><p><strong>+------+</strong></p><p><strong>| NULL</strong> <strong>|</strong></p><p><strong>+------+</strong></p><p><strong>1 row in</strong> <strong>set</strong> <strong>(0.00 sec)</strong></p><p> </p><p><strong>示例2:定义异常处理情况下:</strong></p><p><strong>DELIMITER //</strong></p><p><strong>CREATE</strong> <strong>PROCEDURE</strong> <strong>handlerInsertWithException()</strong></p><p><strong>BEGIN</strong></p><p><strong> DECLARE</strong> <strong>CONTINUE</strong> <strong>HANDLER FOR</strong> <strong>SQLSTATE '23000'</strong> <strong>SET</strong> <strong>@x2=1;</strong></p><p><strong> SET</strong> <strong>@x=1;</strong></p><p><strong> INSERT</strong> <strong>INTO</strong> <strong>location VALUES</strong> <strong>(1,'Beijing');</strong></p><p><strong> SET</strong> <strong>@x=2;</strong></p><p><strong> INSERT</strong> <strong>INTO</strong> <strong>location VALUES</strong> <strong>(1,'Wuxi');</strong></p><p><strong> SET</strong> <strong>@x=3;</strong></p><p><strong>END;</strong></p><p><strong>//</strong></p><p><strong>DELIMITER ;</strong></p><p>
调用存储过程与结果:</p><p><strong>mysql> CALL handlerInsertWithException();</strong></p><p><strong>Query OK, 0 rows</strong> <strong>affected (0.09 sec)</strong></p><p> </p><p><strong>mysql> select</strong> <strong>@x;</strong></p><p><strong>+------+</strong></p><p><strong>| @x |</strong></p><p><strong>+------+</strong></p><p><strong>| 3 |</strong></p><p><strong>+------+</strong></p><p><strong>1 row in</strong> <strong>set</strong> <strong>(0.00 sec)</strong></p><p> mysql> select @x,@x2//
+——+——+
| @x | @x2 |
+——+——+
| 3 | 1 |
+——+——+
1 row in set (0.00 sec)
以上显示结果是@x=3,@x2=1,和上面分析的情况相同,说明程序运行无误。</p><p><strong>说明与结论:</strong></p><p>一、MySQL中,@var_name表示用户变量,使用SET语句为其赋值,用户变量与连接有关,一个客户端定义的变量不能被其他客户端看到或者使用。当客户端退出时,该客户端连接的所有变量将自动释放。</p><p>二、在示例1中,由于注释了异常的声明"",此时向表中插入相同主键,就会触发异常,并且采取默认(EXIT)路径;且查看此时的@x返回2,表示下面的INSERT语句并没有执行就退出了.</p><p>三、定义了异常处理,此时遇到错误也会按照异常定义那样继续执行;但只有第一条数据被插入到表中,此时用户变量@x=3说明已经执行到了结尾;</p><p><strong>DECLARE CONDITION</strong><strong>环境声明
</strong> 在进行错误处理的时候,可以使用SQLSTATE或指定一个错误代码,实际上,也可以给他们定义一个名字,然后在进行处理的时候使用定义的名字。比如看下面这个例子:
mysql> create procedure pro18()
-> begin
-> declareconstraint violation
condition for sqlstate ’23000′;#注意constraint violation两侧的是“”,即Tab键上边,1键左边的按键,不是单引号,写成单引号会报错<br/> -> declare exit handler for
constraint violationrollback;#注意constraint violation两侧的是“
”
-> start transaction;
-> insert into t2(s1) values(1);
-> insert into t2(s1) values(1);
-> commit;
-> end;//
Query OK, 0 rows affected (0.00 sec)</p><p>首先给sqlstate ’23000′定义了一个名字constraint violation
,在进行操作的时候,就直接可以用这个名字了。t2表是一个innodb表,所以对这个表的插入操作都会ROLLBACK回滚,在这 里例子中,由于主键插入两个同样的值导致sqlstate 23000错误发生,导致回滚事件发生。sqlstate 23000是约束错误。
下面调用这个存储过程并查看运行结果:
mysql> call pro18()//
Query OK, 0 rows affected (0.04 sec)</p><p>mysql> select * from t2//
Empty set (0.01 sec)
可以看到t2中没有插入任何记录,全部事务都回滚了。</p><p> 下面再来介绍几个声明条件,首先看例子:
mysql> create procedure pro19()
-> begin
-> declare exit handler for not found begin end;
-> declare exit handler for sqlexception begin end;
-> declare exit handler for sqlwarning begin end;
-> end;//
Query OK, 0 rows affected (0.00 sec)
这个例子展示了三个预条件声明:NOT FOUNT是找不到行,SQLEXCEPTION是错误,SQLWARNING是警告或注释;这三个条件声明是预声明,所以不需要声明条件就可以使用。但 是如果使用:declare sqlexception condition….这种格式的话,就会报错。</p><p> </p><p>错误代码</p><p>
1011 HY000 Error on delete of ''%s'' (errn %d)
1021 HY000 Disk full (%s); waiting for someone to free some space . . .
1022 23000 Can''t write; duplicate key in table ''%s''
1027 HY000 ''%s'' is locked against change
1036 HY000 Table ''%s'' is read only
1048 23000 Column ''%s'' cannot be null
1062 23000 Duplicate entry ''%s'' for key %d
1099 HY000 Table ''%s'' was locked with a READ lock and can''t be updated
1100 HY000 Table ''%s'' was not locked with LOCK TABLES
1104 42000 The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay
1106 42000 Incorrect parameters to procedure ''%s''
1114 HY000 The table ''%s'' is full
1150 HY000 Delayed insert thread couldn''t get requested lock for table %s
1165 HY000 INSERT DELAYED can''t be used with table ''%s'' because it is locked with LOCK TABLES
1242 21000 Subquery returns more than 1 row
1263 22004 Column set to default value; NULL supplied to NOT NULL column ''%s'' at row %ld
1264 22003 Out of range value adjusted for column ''%s'' at row %ld
1265 1000 Data truncated for column ''%s'' at row %ld
1312 0A000 SELECT in a stored program must have INTO
1317 70100 Query execution was interrupted
1319 42000 Undefined CONDITION: %s
1325 24000 Cursor is already open
1326 24000 Cursor is not open
1328 HY000 Incorrect number of FETCH variables
1329 2000 No data to FETCH
1336 42000 USE is not allowed in a stored program
1337 42000 Variable or condition declaration after cursor or handler declaration
1338 42000 Cursor declaration after handler declaration
1339 20000 Case not found for CASE statement
1348 HY000 Column ''%s'' is not updatable
1357 HY000 Can''t drop a %s from within another stored routine
1358 HY000 GOTO is not allowed in a stored program handler
1362 HY000 Updating of %s row is not allowed in %s trigger
1363 HY000 There is no %s row in %s trigger </p><p>存储过程是<a>mysql</a>高级编程的一大特色,当然存储过程包括了很多的知识,包括错误处理,预定义等,下面让我们首先看一下错误处理的部分。</p><p><strong>定义错误:</strong></p><p>为错误定义一个名称,语法为:</p><pre>DECLARE error_name CONDITION FOR condition_value;</pre><pre>declare 定义一个变量</pre><pre>error_name:自定义的错误的名字</pre><pre>condition_value可以是两种情况:</pre><pre>第一:直接写错误号,如 1305;</pre><pre>
</pre><p>第二:写sqlstate错误号: 如</p><pre>SQLSTATE '42000';</pre><pre>
</pre><p><strong>错误处理</strong></p><p>语法为:</p><pre>DECLARE handler_type HANDLER FOR condition_value</pre><pre>begin</pre><pre>...</pre><pre>end;</pre><pre>handler_type: 处理的过程。</pre><pre> CONTINUE 继续执行未完成的存储过程,直至结束。(常用,默认)</pre><pre> | EXIT 出现错误即自动跳出所在的begin不再执行后面的语句。</pre><pre>condition_value: 处理的触发条件</pre><pre> SQLSTATE [VALUE] sqlstate_value 不用说了,就是上面提到的第二中方法,也是最常用的错误定义,自己去查错误列表吧。</pre><pre> | condition_name 我们刚刚定义的那个名字errorname就是用在这里的。</pre><pre> | SQLWARNING 代表所有以01开头的错误代码</pre><pre> | NOT FOUND 表所有以02开头的错误代码,当然也可以代表一个游标到达数据集的末尾。</pre><pre> | SQLEXCEPTION 代表除了SQLWARNING和NOT FOUND 的所有错误代码。</pre><pre> | mysql_error_code 错误编号,上面的第一种方法,不过同样可以在错误列表从中查到,是我比较常用的。</pre><pre>
</pre><pre>例子:</pre><pre>create procedure error_test()</pre><pre>begin</pre><pre>#定义错误,1305是调用了错误的存储过程</pre><pre>declare errname condition for 1305;</pre><pre>declare continue handler for errname</pre><pre>begin</pre><pre>select 'no that procedure' as error;</pre><pre>end;</pre><pre>call aaa();end;</pre><pre>备注:</pre><pre>如果需要查看更多的错误列表可以直接到MySQL安装路径下。</pre><pre>比如我的/usr/local/mysql/share/mysql/errmsg.txt</pre><pre>说明:SQLSTATE [VALUE] sqlstate_value这种格式是专门为ANSI SQL 和 ODBC以及其他的标准.</pre><p>并不是所有的MySQL ERROR CODE 都映射到SQLSTATE。</p><p>
</p>
网友评论