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号相加。