mysql Error Handling and Raising in Stored Procedures

MySQL的存储过程错误捕获方式和Oracle的有很大的不同。

MySQL中可以使用DECLARE关键字来定义处理程序。其基本语法如下:

DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement 

handler_type: CONTINUE | EXIT  

condition_value: 
SQLSTATE [VALUE] sqlstate_value |
condition_name | 
SQLWARNING | 
NOT FOUND  | 
SQLEXCEPTION | 
mysql_error_code 

handler_type参数指明错误的处理方式,该参数有2个取值。这1个取值分别是CONTINUE、EXIT

CONTINUE表示遇到错误,执行预先定义的方式,继续向下执行;

EXIT表示遇到错误后,执行预先定义的方式,马上退出;

注意:通常情况下,执行过程中遇到错误应该立刻停止执行下面的语句,并且撤回前面的操作。

condition_value参数指明错误类型,该参数有6个取值。

sqlstate_value和mysql_error_code与条件定义中的是同一个意思。

condition_name是DECLARE定义的条件名称。

SQLWARNING表示所有以01开头的sqlstate_value值。

NOT FOUND表示所有以02开头的sqlstate_value值。

SQLEXCEPTION表示所有没有被SQLWARNING或NOT FOUND捕获的sqlstate_value值。

sp_statement表示一些存储过程或函数的执行语句。

下面是定义处理程序的几种方式。代码如下:

//方法一:捕获sqlstate_value 
DECLARE CONTINUE HANDLER FOR SQLSTATE '42000'
SET @info='CAN NOT FIND'; 
//方法二:捕获mysql_error_code 
DECLARE CONTINUE HANDLER FOR 1148SET @info='CAN NOT FIND'; 
//方法三:先定义条件,然后调用 
DECLARE can_not_find CONDITION FOR 1146 ; 
DECLARE CONTINUE HANDLER FOR can_not_find SET 
@info='CAN NOT FIND'; 
//方法四:使用SQLWARNING 
DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR'; 
//方法五:使用NOT FOUND 
DECLARE EXIT HANDLER FOR NOT FOUND SET @info='CAN NOT FIND'; 
//方法六:使用SQLEXCEPTION 
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR';

上述代码是6种定义处理程序的方法。

第一种方法是捕获sqlstate_value值。如果遇到sqlstate_value值为42000,执行CONTINUE操作,并且输出"CAN NOT FIND"信息。

第二种方法是捕获mysql_error_code值。如果遇到mysql_error_code值为1148,执行CONTINUE操作,并且输出"CAN NOT FIND"信息。

第三种方法是先定义条件,然后再调用条件。这里先定义can_not_find条件,遇到1148错误就执行CONTINUE操作。

第四种方法是使用SQLWARNING。SQLWARNING捕获所有以01开头的sqlstate_value值,然后执行EXIT操作,并且输出"ERROR"信息。

第五种方法是使用NOT FOUND。NOT FOUND捕获所有以02开头的sqlstate_value值,然后执行EXIT操作,并且输出"CAN NOT FIND"信息。

第六种方法是使用SQLEXCEPTION。SQLEXCEPTION捕获所有没有被SQLWARNING或NOT FOUND捕获的sqlstate_value值。

 

单句的

DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_row_found = 1;
DECLARE CONTINUE HANDLER FOR 1062
SELECT 'Error, duplicate key occurred';

如果错误捕获的时候要做的操作比较多可以这样

DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT 'An error has occurred, operation rollbacked and the stored procedure was terminated';
END;

example:

CREATE TABLE `article_tags` (
  `article_id` int(11) NOT NULL DEFAULT '0',
  `tag_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`article_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DELIMITER $$
 
CREATE PROCEDURE insert_article_tags(IN article_id INT, IN tag_id INT)
BEGIN
 
    DECLARE CONTINUE HANDLER FOR 1062
    SELECT CONCAT('duplicate keys (',article_id,',',tag_id,') found') AS msg;
 
    -- insert a new record into article_tags
    INSERT INTO article_tags(article_id,tag_id)
    VALUES(article_id,tag_id);
 
    -- return tag count for the article
    SELECT COUNT(*) FROM article_tags;
END

mysql 抛出错误给调用程序

使用signal语句返回Error或warning给存储过程的调用者。

signal的语法:

SIGNAL SQLSTATE | condition_name
SET condition_information_item_name_1 = value_1,
    condition_information_item_name_1 = value_2, etc;

使用SET设置返回的内容,如果要返回多个条件信息用逗号隔开。

RESIGNAL无论是功能上还是用法上和signal都很像,除了:

  1.使用resignal的时候必须配合handler使用,否则会得到 “RESIGNAL when handler is not active”的报错,

然而signal可以在存储过程的任何地方使用。

  2.可以漏掉resignal语句的所有属性,甚至是SQLSTATE value.

DELIMITER $$
 
CREATE PROCEDURE AddOrderItem(
                 in orderNo int,
             in productCode varchar(45),
             in qty int, 
                         in price double, 
                         in lineNo int )
BEGIN
    DECLARE C INT;
 
    SELECT COUNT(orderNumber) INTO C
    FROM orders 
    WHERE orderNumber = orderNo;
 
    -- check if orderNumber exists
    IF(C != 1) THEN 
        SIGNAL SQLSTATE '45000'
            SET MESSAGE_TEXT = 'Order No not found in orders table';
    END IF;
    -- more code below
    -- ...
END
DELIMITER $$
 
CREATE PROCEDURE Divide(IN numerator INT, IN denominator INT, OUT result double)
BEGIN
    DECLARE division_by_zero CONDITION FOR SQLSTATE '22012';
 
    DECLARE CONTINUE HANDLER FOR division_by_zero 
    RESIGNAL SET MESSAGE_TEXT = 'Division by zero / Denominator cannot be zero';
    -- 
    IF denominator = 0 THEN
        SIGNAL division_by_zero;
    ELSE
        SET result := numerator / denominator;
    END IF;
END

没有resignal 的时候调用存储过程:

DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `insert_article_tags`(IN article_id INT, IN tag_id INT)
BEGIN
 
     DECLARE CONTINUE HANDLER FOR 1062
        begin
        insert into pro_log values('1062',CONCAT('duplicate keys (',article_id,',',tag_id,') found'));
        SELECT CONCAT('duplicate keys (',article_id,',',tag_id,') found') AS msg;
    -- resignal set message_text ='duplicate keys';
        end ;

   -- declare continue handler for sqlexception
   -- SELECT 'SQLException invoked';

    -- insert a new record into article_tags
    INSERT INTO article_tags(article_id,tag_id)
    VALUES(article_id,tag_id);
 
    -- return tag count for the article
SELECT 
    COUNT(*)
FROM
    article_tags;
   
END

测试调用

 去掉注释的 resignal set。

继续测试

这样可以得到向外抛出的错误。

参考:

http://www.mysqltutorial.org/mysql-error-handling-in-stored-procedures/

http://www.mysqltutorial.org/mysql-signal-resignal/

http://www.cnblogs.com/lyhabc/p/3793524.html

http://www.zhdba.com/mysqlops/2013/08/31/mysql-handler-2/

posted @ 2014-09-17 20:25  Alex-Zeng  阅读(347)  评论(0编辑  收藏  举报