简单地说,存储过程就是一条或多条 SQL 语句的集合,可视为批文件,但是其作用不仅限于批处理。存储程序可以分为存储过程和函数。存储过程要用 CALL 语句来调用,并且只能用输出变量返回值。
语法格式:
CREATE PROCEDURE SP_name ( [ proc_parameter ] )
[ characteristics ... ] routine_body
【注释】
a. CREATE PROCEDURE: 创建存储过程的关键字。
b. sp_name: 存储过程的名字。
c. proc_parameter:为存储过程的参数列表,列表形式如下:
[ IN | out | INOUT ] param_name type
IN 表示输入参数;OUT 表示输出参数;INOUT 表示即可以输入也可以输出参数;param_name 表示参数名;type 表示参数类型 。e. routine body是SQL代码的内容,可以用BEGIN..END来表示SQL代码的开始和结束。
【例】创建查看 fruits 表的存储过程。SQL 语句如下:
mysql> DELIMITER //
mysql> CREATE PROCEDURE proc()
-> BEGIN
-> SELECT * FROM fruits;
-> END //
Query OK, 0 rows affected (0.35 sec)
mysql> DELIMITER ;
这个存储过程和使用 SELECT 语句查看表得到的结果是一样的, 当然存储过程也可以是很多语句的复杂组合,其本身也可以调用其他的函数,来组成更加复杂的操作。
【注】 "DELIMITER //”语句的作用是将 MySQL 的结束符设置为 // ,因为 MySQL 默认的语句结束符号为分号(;),为了避免与存储过程中的 SQL 语句结束符冲突,需要使用 DELIMITER 改变存储过程的结束符,并以“END //”结束存储过程,存储过程,定义完毕之后再使用 DELIMITER 恢复默认结束符。DELIMITER 也可以指定其他符号作为结束符。
【例】 创建名为 countproc 的存储过程,用于获取 fruits 表中的记录数。代码如下:
mysql> DELIMITER //
mysql> CREATE PROCEDURE countproc ( OUT paraml INT )
-> BEGIN
-> SELECT COUNT(*) INTO paraml FROM fruits;
-> END //
Query OK, 0 rows affected (0.07 sec)
mysql> DELIMITER ;
语法格式:
CREATE FUNCTION func_name ( [ func_parameter ] )
RETURNS type
[ characteristics ... ] routine_body
其中,IN 表示输入参数; OUT 表示输出参数; INOUT 表示既可以输入也可以输出参数; param_name 表示参数名称; type 表示参数的类型,该类型可以是 MySQL 数据库中的任意类型。
【例】 创建存储函数,名称为 namebyzip, 该函数返回 SELECT 语句的查询结果,数值类型为字符串型。代码如下:
mysql> DELIMITER //
mysql> CREATE FUNCTION namebyzip()
-> RETURNS CHAR(50)
-> RETURN ( SELECT s_name FROM suppliers WHERE s_call='48075');
-> //
Query OK, 0 rows affected (0.11 sec)
mysql> DELIMITER ;
变量可以在子程序中声明并使用,这些变量的作用范围是在 BEGIN ... END 程序中。
语法格式:
DECLARE var_namel [, var_name] ... date_type [ DEFAULT value ];
【例】定义名称为 myparam 的变量,类型为 INT,默认值为100。代码如下:
DECLARE myparam INT DEFAULT 100;
变量定义之后,为变量赋值可以改变量的默认值,MysSQL中使用 SET 语句为变量赋值,语法格式如下:
SET var_name = expr [ , var_name = expr ] ... ;
【例】声明3个变量,分别为 varl、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;
【例】声明变量 fruit_name 和 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';
定义条件是事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时应当采取的处理方式,并且保证存储过程或函数在遇到警告或错误时能继续执行。这样可以增强存储程序处理问题的能力,避免程序异常停止运行。
【例】定义 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
语法格式如下:
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
【注释】
(1) hander_type 为错误处理方式,参数取3个值: CONTINUE、 EXIT 和 UNDO. CONTINUE 表示遇到错误不处理,继续执行; EXIT 表示遇到错误马上退出; UNDO 表示遇到错误后撒回之前的操作,MySOL中暂时不支持这样的操作。
(2) condition_value 表示错误类型,可以有以下取值:
【例】定义处理程序的几种方式。代码如下:
//方法一:捕获sqlstate_value
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @info='NO_SUCH_TABLE';
第一种方法是捕获 sqlstate_value 值。如果遇到 sqlstate_value 值为 42S02, 执行 CONTINUE 操作,并且输出 NO_ SUCH_TABLE 信息。
//方法二: 捕获 mysql_error_code
DECLARE CONTINUE HANDLER FOR 1146 SET @info= 'NO_SUCH_TABLE' ;
第二种方法是捕获 mysql_error_code值。如果遇到 mysql_error_code值为 1146, 执行 CONTINUE 操作,并且输出NO_ SUCH_TABLE 信息。
//方法三: 先定义条件,然后调用
DECLARE no_such_table CONDITION FOR 1146;
DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SET @info= 'NO_SUCH_TABLE' ;
第三种方法是先定义条件,然后再调用条件。这里先定义 no_such_table 条件,遇到 1146 错误就执行 CONTINUE 操作。
//方法四: 使用 SQLWARNING
DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';
第四种方法是使用 SQLWARNING。SQLWARNING 捕获所有以 01 开头的 sqlstate_value 然后执行 EXIT 操作,并且输出 ERROR 信息。
//方法五: 使用 NOT FOUND
DECLARE EXIT HANDLER FOR NOT FOUND SET @info= 'NO_SUCH_TABLE' ;
第五种方法是使用 NOT FOUND。NOT FOUND 捕获所有以 02 开头的 sqlstate_value 值然后执行 EXIT 操作,并且输出NO_SUCH_TABLE 信息。
//方法六: 使用 SOLEXCEPTION
DECLARE EXIT HANDLER FOR SQLEXCEPTON SET @infom='ERROR';
第六种方法是使用 SQLEXCEPTION。SQLEXCEPTION 捕获所有未被 SQLWARNING 或 NOT FOUND 捕获的 sqlstate_ value值, 然后执行 EXIT 操作,并且输出 ERROR 信息。
【例】定义条件和处理程序。
mysql> CREATE TABLE t(s1 int,primary key(s1));
Query OK, 0 rows affected
mysql> DELIMITER //
mysql> CREATE PROCEDURE handlerdemo()
-> BEGIN
-> DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2=1;
-> SET @x=1;
-> INSERT INTO t VALUES(1);
-> SET @x=2;
-> INSERT INTO t VALUES(1);
-> SET @x=3;
-> END;
-> //
Query OK, 0 rows affected
mysql> DELIMITER ;
mysql> CALL handlerdemo();
Query OK, 0 rows affected
mysql> SELECT @x;
+----+
| @x |
+----+
| 3 |
+----+
1 row in set
查询语句可能返回多条记录,如果数据量非常大,需要在存储过程和存储函数中使用光标来逐条读取查询结果集中的记录。
光标必须在声明处理程序之前被声明,并且变量和条件还必须在声明光标或处理程序之前被声明。
语法格式:
DECLARE cursor_name CURSOR FOR selec_statement
【例】 声明名称为 cursor_fruit 的光标。代码如下:
DECLARE cursor_fruits CURSOR FOR SELECT f_name,f_price FROM fruits;
光标的名称为 cursor_fruit, SELECT 语句部分从 fruits 表中查询出 f_name 和 f_price 字段的值。
语法格式:
OPEN cursor_name
【例】打开名为 cursor_ fruit 的光标。代码如下:
OPEN cursor_fruits;
FETCH cursor_name INTO var_name [ , var_name ] ... {参数名称}
【例】 使用名为 cursor_fruit 的光标查询,并将查询出来的数据存入 fruit_name 和 fruit_price 两个变量中。
代码如下:
FETCH cursor_fruits INTO fruits_name,fruits_price;
上面的示例中,将光标 cursor_fruit 中 SELECT 语句在询出来的信息存入 fruit_name 和 fruit_price 中。fruit_name 和 fruit_price 必须在前面已经定义。
CLOSE cursor_name{光标名称}
如果光标未被明确地关闭,就会在声明的复合语句的未尾被关闭 。
【例】关闭名为 cursor_fruit 的光标。代码如下:
CLOSE cursor_fruit;
【注】 MySQL中光标只能在存储过程和函数中使用。
存储过程与自定义函数的区别
【注】参考于清华大学出版社《MySQL数据库应用案例课堂》2016年1月第1版