MySQL:存储过程和函数

时间:2022-05-26 14:08:50
【例10.1】创建查看fruits表的存储过程,代码如下:
CREATE PROCEDURE Proc()
     BEGIN
        SELECT * FROM fruits;
     END ;
这行代码创建了一个查看fruits表的存储过程,每次调用这个存储过程的时候都会执行SELECT语句查看表的内容,代码的执行过程如下:
 DELIMITER //
 CREATE PROCEDURE Proc()
     BEGIN
     SELECT * FROM fruits;
     END //
Query OK, 0 rows affected (0.00 sec)

 DELIMITER ;

【例10.2】创建名称为CountProc的存储过程,代码如下:
CREATE PROCEDURE CountProc (OUT param1 INT)
BEGIN
SELECT COUNT(*) INTO param1 FROM fruits;
END;
上述代码的作用是创建一个获取fruits表记录条数的存储过程,名称是CountProc,COUNT(*) 计算后把结果放入参数param1中。代码的执行结果如下:
 DELIMITER //
 CREATE PROCEDURE CountProc(OUT param1 INT)
  BEGIN
  SELECT COUNT(*) INTO param1 FROM fruits;
  END //
Query OK, 0 rows affected (0.00 sec)
 DELIMITER ;
当使用DELIMITER命令时,应该避免使用反斜杠(’\’)字符,因为反斜线是MySQL的转义字符。

【例10.3】创建存储函数,名称为NameByZip,该函数返回SELECT语句的查询结果,数值类型为字符串型,代码如下:
CREATE FUNCTION NameByZip ()
 RETURNS CHAR(50)
 RETURN  (SELECT s_name FROM suppliers WHERE s_call= '48075');
创建一个f的存储函数,参数定义为空,返回一个INT类型的结果。代码的执行结果如下:
 DELIMITER //
 CREATE FUNCTION NameByZip()
 RETURNS CHAR(50)
 RETURN   (SELECT s_name FROM suppliers WHERE s_call= '48075');
 //
Query OK, 0 rows affected (0.12 sec)

 DELIMITER ;

【例10.4】定义名称为myparam的变量,类型为INT类型,默认值为100,代码如下:
DECLARE  myparam  INT  DEFAULT 100;

【例10.5】声明3个变量,分别为var1、var2和var3,数据类型为INT,使用SET为变量赋值,代码如下:
DECLARE var1, var2, var3 INT;
SET var1 = 10, var2 = 20;
SET var3 = var1 + var2;
MySQL中还可以通过SELECT ... INTO为一个或多个变量赋值,语法如下:
SELECT col_name[,...] INTO var_name[,...] table_expr;
这个SELECT语法把选定的列直接存储到对应位置的变量。col_name表示字段名称;var_name表示定义的变量名称;table_expr表示查询条件表达式,包括表名称和WHERE子句。
【例10.6】声明变量fruitname和fruitprice,通过SELECT ... INTO语句查询指定记录并为变量赋值,代码如下:
DECLARE fruitname CHAR(50);
DECLARE fruitprice DECIMAL(8,2);

SELECT f_name,f_price INTO fruitname, fruitprice
FROM fruits WHERE f_id ='a1';

【例10.7】定义"ERROR 1148(42000)"错误,名称为command_not_allowed。可以用两种不同的方法来定义,代码如下:
//方法一:使用sqlstate_value
DECLARE  command_not_allowed CONDITION FOR SQLSTATE '42000';
//方法二:使用mysql_error_code
DECLARE  command_not_allowed CONDITION  FOR  1148
2.定义处理程序
定义处理程序时,使用DECLARE语句的语法如下:
DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement
handler_type:
    CONTINUE | EXIT | UNDO

condition_value:
    SQLSTATE [VALUE] sqlstate_value
 | condition_name
 | SQLWARNING
 | NOT FOUND
 | SQLEXCEPTION
 | mysql_error_code
【例10.8】定义处理程序的几种方式,代码如下:
//方法一:捕获sqlstate_value
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @info='NO_SUCH_TABLE';

//方法二:捕获mysql_error_code
DECLARE CONTINUE HANDLER FOR 1146 SET @info=' NO_SUCH_TABLE ';

//方法三:先定义条件,然后调用
DECLARE  no_such_table  CONDITION  FOR  1146;
DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SET @info=' NO_SUCH_TABLE ';

//方法四:使用SQLWARNING
DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';

//方法五:使用NOT FOUND
DECLARE EXIT HANDLER FOR NOT FOUND SET @info=' NO_SUCH_TABLE ';

//方法六:使用SQLEXCEPTION
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR';

【例10.9】定义条件和处理程序,具体执行的过程如下:
 CREATE TABLE test.t (s1 int,primary key (s1));
Query OK, 0 rows affected (0.00 sec)

 DELIMITER //
 
 CREATE PROCEDURE handlerdemo ()
      BEGIN
       DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
       SET @x = 1;
       INSERT INTO test.t VALUES (1);
       SET @x = 2;
       INSERT INTO test.t VALUES (1);
       SET @x = 3;
     END;
     //
Query OK, 0 rows affected (0.00 sec)
 
 DELIMITER ;

/*调用存储过程*/
 CALL handlerdemo();
Query OK, 0 rows affected (0.00 sec)
/*查看调用过程结果*/
 SELECT @x;
    +------+
    | @x   |
    +------+
    | 3    |
    +------+
    1 row in set (0.00 sec)
【例10.10】声明名称为cursor_fruit的光标,代码如下:
DECLARE cursor_fruit CURSOR FOR SELECT f_name, f_price FROM fruits ;
上面的示例中,光标的名称为cur_fruit,SELECT语句部分从fruits表中查询出f_name和f_price字段的值。
【例10.12】使用名称为cursor_fruit的光标。将查询出来的数据存入fruit_name和fruit_price这两个变量中,代码如下:
FETCH  cursor_fruit INTO fruit_name, fruit_price ;
上面的示例中,将光标cursor_fruit中SELECT语句查询出来的信息存入fruit_name和fruit_price中。fruit_name和fruit_price必须在前面已经定义。

【例10.13】关闭名称为cursor_fruit的光标,代码如下:
CLOSE  cursor_fruit;

【例10.14】IF语句的示例,代码如下:
IF val IS NULL
  THEN SELECT 'val is NULL';
  ELSE SELECT 'val is not NULL';
END IF;
【例10.15】使用CASE流程控制语句的第1种格式,判断val值等于1、等于2,或者两者都不等,语句如下:
CASE val
  WHEN 1 THEN SELECT 'val is 1';
  WHEN 2 THEN SELECT 'val is 2';
  ELSE SELECT 'val is not 1 or 2';
END CASE;
当val值为1时,输出字符串“val is 1”;当val值为2时,输出字符串“val is 2”;否则输出字符串“val is not 1 or 2”。
CASE语句的第2种格式如下:
CASE
    WHEN expr_condition THEN statement_list
    [WHEN expr_condition THEN statement_list] ...
    [ELSE statement_list]
END CASE
【例10.16】使用CASE流程控制语句的第2种格式,判断val是否为空、小于0、大于0或者等于0,语句如下:
CASE
  WHEN val IS NULL THEN SELECT 'val is NULL';
  WHEN val < 0 THEN SELECT 'val is less than 0';
  WHEN val > 0 THEN SELECT 'val is greater than 0';
  ELSE SELECT 'val is 0';
END CASE;
当val值为空,输出字符串“val is NULL”;当val值小于0时,输出字符串“val is less than 0”;当val值大于0时,输出字符串“val is greater than 0”;否则输出字符串“val is 0”。

【例10.17】使用LOOP语句进行循环操作,id值小于等于10之前,将重复执行循环过程,代码如下:
DECLARE id INT DEFAULT 0;
add_loop: LOOP  
SET id = id + 1;
  IF id >= 10 THEN  LEAVE add_loop;
  END IF;
END LOOP add_ loop;

【例10.18】使用LEAVE语句退出循环,代码如下:
add_num: LOOP  
SET @count=@count+1;
IF @count=50 THEN LEAVE add_num ;
END LOOP add_num ;
该示例循环执行count加1的操作。当count的值等于50时,使用LEAVE语句跳出循环。

【例10.19】ITERATE语句示例,代码如下:
CREATE PROCEDURE doiterate()
BEGIN
DECLARE p1 INT DEFAULT 0;
my_loop: LOOP
  SET p1= p1 + 1;
  IF p1 < 10 THEN ITERATE my_loop;
  ELSEIF p1 > 20 THEN LEAVE my_loop;
  END IF;
  SELECT 'p1 is between 10 and 20';
END LOOP my_loop;
END

【例10.20】REPEAT语句示例,id值小于等于10之前,将重复执行循环过程,代码如下:
DECLARE id INT DEFAULT 0;
REPEAT
SET id = id + 1;
UNTIL  id >= 10
END REPEAT;
该示例循环执行id加1的操作。当id值小于10时,循环重复执行;当id值大于或者等于10时,使用LEAVE语句退出循环。REPEAT循环都以END REPEAT结束。

【例10.21】WHILE语句示例,id值小于等于10之前,将重复执行循环过程,代码如下:
DECLARE i INT DEFAULT 0;
WHILE i < 10 DO
SET i = i + 1;
END WHILE;

【例10.22】定义名为CountProc1的存储过程,然后调用这个存储过程,代码执行如下:
定义存储过程:
 DELIMITER //
 CREATE PROCEDURE CountProc1 (IN sid INT, OUT num INT)
     BEGIN
       SELECT COUNT(*) INTO num FROM fruits WHERE s_id = sid;
     END //
Query OK, 0 rows affected (0.00 sec)

  DELIMITER ;
调用存储过程:
 CALL CountProc1 (101, @num);
Query OK, 1 row affected (0.00 sec)
查看返回结果:
 select @num;
+------+
| @num |
+------+
|    3 |
+------+
1 row in set (0.02 sec)
该存储过程返回了指定s_id=101的水果商提供的水果种类,返回值存储在num变量中,使用SELECT查看,返回结果为3。

【例10.23】定义存储函数CountProc2,然后调用这个函数,代码如下:
 DELIMITER //
 CREATE FUNCTION  CountProc2 (sid INT)
     RETURNS INT
     BEGIN
     RETURN (SELECT COUNT(*) FROM fruits WHERE s_id = sid);
     END //
Query OK, 0 rows affected (0.00 sec)
  DELIMITER ;
调用存储函数:
 SELECT CountProc2(101);
+--------------------+
| Countproc(101) |
+--------------------+
|             3 |
+-------------------+

【例10.24】SHOW STATUS语句示例,代码如下:
SHOW PROCEDURE STATUS LIKE 'C%'\G

【例10.25】SHOW CREATE语句示例,代码如下:
SHOW CREATE FUNCTION test.CountProc \G

【例10.26】从Routines表中查询名称为CountProc的存储函数的信息,代码如下:
SELECT * FROM information_schema.Routines
WHERE ROUTINE_NAME='CountProc'  AND  ROUTINE_TYPE = 'FUNCTION' \G

【例10.27】修改存储过程CountProc的定义。将读写权限改为MODIFIES SQL DATA,并指明调用者可以执行,代码如下:
ALTER  PROCEDURE  CountProc  
MODIFIES SQL DATA
SQL SECURITY INVOKER ;
执行代码,并查看修改后的信息。结果显示如下:
//执行ALTER PROCEDURE语句
 ALTER  PROCEDURE  CountProc
     MODIFIES SQL DATA  
     SQL SECURITY INVOKER ;
Query OK, 0 rows affected (0.00 sec)  
//查询修改后的CountProc表信息  
 SELECT SPECIFIC_NAME,SQL_DATA_ACCESS,SECURITY_TYPE
      FROM information_schema.Routines
     WHERE ROUTINE_NAME='CountProc' AND ROUTINE_TYPE='PROCEDURE';

【例10.28】修改存储函数CountProc的定义。将读写权限改为READS SQL DATA,并加上注释信息“FIND NAME”,代码如下:
ALTER  FUNCTION  CountProc
READS SQL DATA  
COMMENT 'FIND NAME' ;
执行代码,并查看修改后的信息。结果显示如下:
//执行ALTER FUNCTION语句
 ALTER  FUNCTION  CountProc  
     READS SQL DATA  
     COMMENT 'FIND NAME' ;  
Query OK, 0 rows affected (0.00 sec)  
//查询修改后f表的信息
 SELECT SPECIFIC_NAME,SQL_DATA_ACCESS,ROUTINE_COMMENT
FROM information_schema.Routines
WHERE ROUTINE_NAME='CountProc'  AND  ROUTINE_TYPE = 'FUNCTION'  ;

【例10.29】删除存储过程和存储函数,代码如下:
DROP PROCEDURE CountProc;
DROP FUNCTION CountProc;
语句的执行结果如下:
 DROP PROCEDURE CountProc;
Query OK, 0 rows affected (0.00 sec)
 DROP FUNCTION CountProc;
Query OK, 0 rows affected (0.00 sec)
上面语句的作用就是删除存储过程CountProc和存储函数CountProc。



综合案例

创建一个sch表,并且向sch表中插入表格中的数据,代码如下:
CREATE TABLE sch(id INT, name VARCHAR(50),glass VARCHAR(50));
INSERT INTO sch VALUE(1,'xiaoming','glass 1'), (2,'xiaojun','glass 2');
通过命令DESC命令查看创建的表格,结果如下:
 DESC sch;
+-------+-----------------+------+-------+---------+-------+
| Field | Type        | Null | Key  | Default | Extra |
+-------+-----------------+------+-------+---------+-------+
| id    | int(11)      | YES |      | NULL |      |
| name  | varchar(50)  | YES |     | NULL |      |
| glass  | varchar(50)  | YES |     | NULL |      |
+--------+----------------+------+-------+---------+-------+
3 rows in set (0.00 sec)
通过SELECT * FROM sch来查看插入表格的内容,结果如下:
+------+------------+-----------+
| id   | name    | glass   |
+------+------------+-----------+
|    1 | xiaoming | glass 1  |
|    2 | xiaojun  | glass 2  |
+------+------------+-----------+
  创建一个存储函数用来统计表sch中的记录数。
创建一个可以统计表格内记录条数的存储函数,函数名为count_sch(),代码如下:
CREATE FUNCTION count_sch()
RETURNS INT
RETURN (SELECT COUNT(*) FROM sch);
执行的结果如下:
 DELIMITER //
 CREATE FUNCTION count_sch()
     RETURNS INT
     RETURN (SELECT COUNT(*) FROM sch);
     //
Query OK, 0 rows affected (0.12 sec)
 SELECT count_sch() //
 DELIMITER ;
+-------------+
| count_sch() |
+-------------+
|      2   |
+-------------+
1 row in set (0.05 sec)
创建的储存函数名称为count_sch,通过SELCET count_sch()查看函数执行的情况,这个表中只有两条记录,得到的结果也是两条记录,说明存储函数成功的执行。
  创建一个存储过程,通过调用存储函数的方法来获取表sch中的记录数和sch表中id的和。
创建一个存储过程add_id在同时使用前面创建的存储函数返回表sch中的记录数,计算出表中所有的id之和。代码如下:
CREATE PROCEDURE add_id(out count INT)
BEGIN
DECLARE itmp INT;
DECLARE cur_id CURSOR FOR SELECT id FROM sch;
DECLARE EXIT HANDLER FOR NOT FOUND CLOSE cur_id;

SELECT count_sch() INTO count;

SET @sum=0;
OPEN cur_id;
REPEAT
FETCH cur_id INTO itmp;
IF itmp<10
THEN SET  @sum=  @sum+itmp;
END IF;
UNTIL 0 END REPEAT;
CLOSE cur_id;

END ;
这个存储过程的代码中使用到了变量的声明、光标、流程控制、在存储过程中调用存储函数等知识点,结果应该是两条记录,id之和为3,记录条数是通过上面的存储函数count_sch()获取的,是在存储过程中调用了存储函数。代码的执行情况如下:
 DELIMITER //
 CREATE PROCEDURE add_id(out count INT)
     BEGIN
     DECLARE itmp INT;
     DECLARE cur_id CURSOR FOR SELECT id FROM sch;
     DECLARE EXIT HANDLER FOR NOT FOUND CLOSE cur_id;
     SELECT count_sch() INTO count;
     SET @sum=0;
     OPEN cur_id;
     REPEAT
     FETCH cur_id INTO itmp;
     IF itmp<10
     THEN SET  @sum=  @sum+itmp;
     END IF;
     UNTIL 0 END REPEAT;
     CLOSE cur_id;
     END //
Query OK, 0 rows affected (0.00 sec)

 SELECT @a, @sum  //
+------+--------+
| @a  | @sum |
+------+--------+
|    2 |    3 |
+------+-------+
1 row in set (0.00 sec)
 DELIMITER ;
表sch中只有两条记录,所有id的之和为3,和预想的执行结果完全相同。这个存储过程创建了一个cur_id的光标,使用这个光标来获取每条记录的id,使用REPEAT循环语句来实现所有id号相加。