存储过程就是一条或者多条SQL语句的集合。
create procedure sp_name ([proc_parameter])
[characteristics ...] routine_baby
proc_parameter指定存储过程的参数列表,其语法:
[in|out|inout] param_name type
参数 | 备注 |
---|---|
in | 代表输入参数 |
out | 代表输出参数 |
inout | 既可以输入又可以输出 |
param_name | 参数名称 |
type | 表示参数的类型,可以是MySQL数据库中的任意类型 |
characteristics 为指定存储过程的特性,可以有一下取值:
参数 | 备注 |
---|---|
LANGUAGE SQL | 说明routine_body部分是由SQL语句组成的,当前系统支持的语言是SQL,SQL是LANGUAGE特性的唯一值 |
[NOT]DETERMINISTIC | 指明存储过程执行的结果是否确定,DETERMINISTIC表示结果是确定的,每次执行存储过程时,相同的输入会得到相同的输出。NOT DETERMINISTIC表示结果是不确定的,相同的输入可能得到不同的输出。如果没有指定一个值,那么默认为NOT DETERMINISTIC。 |
{CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA} | 指明子程序所使用的SQL语句的限制。CONTAINS SQL表明子程序包含SQL语言,但是不包含读写数据的语句;NO SQL 表明子程序不包含SQL语句;READS SQL DATA说明子程序包含读数据的语句;MODIFIES SQL DATA表明子程序包含写数据的语句,默认情况下,系统会指定CONTAINS SQL |
SQL SECURITY{DEFINER | INVOKER} | 指明谁有权限来执行。DEFINER表示只有定义者才能执行。INVOKER表示拥有权限的调用者可以执行。 |
COMMENT ‘String’ | 注释信息,可以用来描述存储过程或者函数 |
routine_baby为SQL代码的内容,可以用BIGIN…END来表示SQL代码的开始和结束。
一个返回水果平均价格的示例:
create procedure AvgFruitPrice() -- 存储过程名 AvgFruitPrice
begin
select AVG(f_price) as avgprice from fruits;
end;
语法:
create function func_name ([func_parameter])
returns type
[characteristic ...] routine_body
例子:创建一个返回select 语句的查询结果,数值类型为字符串类型
create function NameByZip()
return char(50)
return ( select s_name from suppliers where s_call = "48075");
要注意的是:
变量作用范围是BEGIN … END。
定义一个变量的语法:
declare var_name[,varname]... data_type [DEFAULT value];
例子:定一个int型,默认100的变量
declare myparam int default 100;
例子:定义3个变量,set赋值
declare var1, var2, var3 int;
set var1 =10, var2 = 20;
set var3 = var2 + var1;
例子:使用INSERT INTO 语句为变量赋值
declare name char(50);
declare price decimal(8,2);
select f_name ,f_price into name,price
from fruits
where f_id = 'a1';
定义条件:实现定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时应该采取的处理方式,并且保证存储过程或函数在遇到警告或者错误时能够继续执行。这样可以增强存储过程处理问题的能力,避免程序异常停止运行。
定义条件使用declare语句:
declare condition_name condition for [condition_type]
[condition type ]:
SQLSTATE [VALUE] sqlstate_value | mysql_error_code
参数 | 备注 |
---|---|
condition_name | 条件的名称 |
condition_type | 参数的类型 |
sqlstate_value 和mysql_error_code | 数值类型错误代码 |
这个语句指定需要特殊处理的条件。它将一个名字和指定的错误条件关联起来,这个名字可以随后被用在定义处理程序的DECLARE HANDLER语句中。
-- 使用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
handler_type 是错误处理方式,取值:
handler_type取值 | 备注 |
---|---|
CONTINUE | 遇到错误不处理,继续执行 |
EXIT | 遇到错误马上退出 |
UNDO | 遇到错误撤回之前的操作,MySQL中暂时不支持这样的操作 |
condition_value表示错误类型,可以有以下取值:
condition_value取值 | 备注 |
---|---|
SQLSTATE[VALUE] sqlstate_value | 包含5个字符的字符串错误值 |
condition_name | 表示DECLARE CONDITION定义的错误条件名称 |
SQLWARNING | 匹配所有以01开头的SQLSTATE错误代码 |
NOT FOUND | 匹配所有以02开头的SQLSTATE错误代码 |
SQL EXCEPTION | 匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码 |
MySQL_error_code | 匹配数值类型的错误代码 |
sp_statement 参数为程序语句段,表示在遇到定义的错误时,需要执行的存储过程或者函数。
定义处理程序的几种方式:
-- [1]捕获sqlstate_value值,如果遇到sqlstate_value值为“42S02”,执行CONTINUE操作,并且输出"NO_SUCH_TABLE"信息。
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @info='NO_SUCH_TABLE'
-- [2]捕获MySQL_error_code值,如果遇到MySQL_error_code值为1146,执行CONTINUE操作,并且输出"NO_SUCH_TABLE"
DECLARE CONTINUE HANDLER FOR 1146 SET @info='NO_SUCH_TABLE';
-- [3]先定义条件,然后调用条件。这里先定义NO_SUCH_TABLE条件,遇到1146错误就执行CONTINUE操作。
DECLARE no_such_table CONDITION FOR 1146;
DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SET @info='NO_SUCH_TABLE';
-- [4]使用SQLWARNING,SQLWARNING捕获所有以01开头的sqlstate_value值,然后执行EXIT操作,并且输出"ERROR"
DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR'
-- [5]使用not found,not found 捕获所有以02开头的sqlstate_value值,然后执行exit操作,并且输出"NO_SUCH_TABLE"信息
DECLARE EXIT HANDLER FOR NOT FOUND SET @info='NO_SUCH_TABLE';
-- [6]使用SQLEXCEPTION。SQLEXCEPTION捕获所有没有被SQLWARNING或者NOT FOUND捕获的sqlstate_value值,然后执行exit操作,并输出"ERROR"信息。
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR'
例子:
-- 创建测试表
create table test.t(
s1 int,
primary key (s1)
);
-- 定义存储过程
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;
-- 调用存储过程
CALL handlerdemo();
mysql> select @x;
+------+
| @x |
+------+
| 3 |
+------+
@x是一个用户变量,执行结果@x等于3,这表明MySQL被执行到程序的末尾,如果“ declare continue handler for sqlstate ‘23000’ set @x2 =1;” 这一行不存在,第二个insert因primary key 强制而失败后,MySQL可能已经采取默认(EXIT)路径,并且SELECT@x可能已经返回2。
@var_name 表示用户变量,使用SET语句为其赋值,用户变量与连接有关,一个客户端定义的变量不能被其他客户端看到或者使用。当客户端退出时,该客户链接的所有变量将被释放。
if语句包含多个条件判断,根据判断的结果为true或者false执行相应的语句,语法格式如下:
IF expr_condition THEN statement_list
[ELSEIF expr_condition THEN statement_list]...
[ELSE statement_list]
END IF
expr_condition求值为真(true),相应的sql语句列表被执行,如果没有expr_condition匹配,则ELSE子句里的语句列表被执行。statement_list可以包括一个或多个语句。
例子:
-- 判断val值是否为空,如果val值为空,则输出字符串"val is null",否则输出字符串"val is not null".
if val is null
then select 'val is null';
else select 'val is not null';
end if;
CASE case_expr
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list]...
[ELSE statement_list]
END CASE
例子:
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;
case
when val is null then select 'val is null';
when val < 0 then select 'val is less then 0';
when val > 0 then select 'val is greater than 0';
else select 'val is 0';
end case;
LOOP循环语句用来重复执行某些语句,与IF和CASE语句相比,LOOP只是创建一个循环的过程,并不进行条件判断,LOOP内的语句一直重复执行知道循环被退出,跳出循环。
[loop_label:] LOOP
statement_list
END LOOP [loop_label]
例子:
-- 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;
leave语句用来退出任何被标注的流程控制结构,leave语句格式如下:
leave label
例子:
add_num : LOOP
SET @count = @count+1;
if @count=50 then leave add_num;
end loop add_num;
iterate语句将执行顺序转到语句段开头处,只可以出现在LOOP、REPEATE和WHILE语句内,意思为“再次循环”,label参数表示循环的标志。ITERATE语句必须跟在循环标志前面。
iterate label
例子:
-- p1 =0 ,如果p1的值小于10时,重复执行p1加1操作, 如果大于10小于20时,打印“p1 is between 10 and 20”,当p1大于20时,退出循环
craete 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
repeat 语句创建一个带条件判断的循环过程,每次语句执行完毕之后,会对条件表达式进行判断,如果表达式为真,则循环结束;否则重复执行循环中的语句。repeat语句的基本格式如下:
[repeat_lable:] REPEAT
statement_list
UNTIL expr_condition
END REPEAT[repeat_label]
repeat_lable为repeat语句的标注名称,该参数可以省略,repeat语句内的语句或者语句群,知道expr_condition为真。
-- 循环执行id加1的操作,当id值小于10时,循环重复执行:当id值大于或者等于10时,退出循环。REOEAT循环都以END REPEAT结束。
declare id int default 0;
repeat
set id = id +1;
until id >=10
end repeat;
while语句创建一个带参数判断的循环过程,与repeat不同,while在执行语句时,先对制定的表达式进行判断,如果为真,则执行循环内的语句,否则退出循环,while语句的基本格式:
[while_label:] WHILE expr_condition DO
statement_list
END while [while_babel]
例子:
-- i值小于10时,将重复执行循环
declare i default 0;
while i <10 DO
SET i=i+1;
END WHILE;
存储过程和函数有多种调用方式,窜出过程必须使用CALL语句调用,并且存储过程和数据库相关,如果执行其他数据库中的存储过程,需要制定数据库名称。例如 CALL dbname.procname
例子:
-- 创建存储过程
create procedure CountProc1 (in sid int,out num int)
begin
select count(*) into num from fruits where s_id = sid;
end
-- 调用存储过程
call CountProcl (101,@num);
-- 查询输出结果
select @num;
例子:
-- 调用存储函数
create function countProc2 (sid int)
returns int
begin
return (select count(*) from fruits where s_id = sid);
end;
-- 调用存储函数
select CountProc2(101);
show [procedure | function] status [like 'pattern']
例子:
mysql> show procedure status like 'C%' \G
*************************** 1. row ***************************
Db: test -- 数据库
Name: CountProc -- 存储过程名
Type: PROCEDURE -- 类型
Definer: root@localhost -- 创建者
Modified: 2018-04-25 16:45:55 -- 修改时间
Created: 2018-04-25 16:45:55 -- 创建时间
Security_type: DEFINER -- 安全类型
Comment: -- 备注
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8_general_ci
1 row in set (0.00 sec)
使用show create 语句查看存储过程和函数定义
mysql> show create procedure test.CountProc \G
*************************** 1. row ***************************
Procedure: CountProc
sql_mode: STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_
TION
Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE
T param1 INT)
BEGIN
select count(*) into param1 from fruits;
END
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8_general_ci
1 row in set (0.00 sec)
语法:
alter {procedure | function} sp_name [characteristic...]
sp_name为存储过程或者函数名称
characteristic参数取值 | 含义 |
---|---|
CONTAINS SQL | 表示子程序包含SQL语句,但不包含读或写语句数据的语句 |
NO SQL | 表示子程序中不包含SQL语句 |
READS SQL DATA | 表示子程序中包含读数据的语句 |
MODIFIES SQL | 表示子程序中包含写数据的语句 |
SQL SECURITY{DEFINER | INVOKER} | 谁有权限来执行 |
DEFINER | 只有定义者自己能执行 |
INVOKER | 表示调用者可以执行 |
COMMENT ‘String’ | 表示注释信息 |
例子:
-- 将读写权限改为modifies sql data,指明调用者可以执行
alter procedure CountProc
modifies sql data
sql security invoker;
-- 修改存储函数CountProc,将读写权限改为READS SQL DATA
alter function CountProc
reads sql data
comment 'find name';
语法:
drop { procedure | function }[if exists] sp_name
例子:
drop procedure CountProc;
drop function CountProc;
本质上他们都是存储程序。
不能,drop掉重新创建。
可以,使用CALL语句调用其他存储过程,但是不能DROP。
在定义存储过程参数列表时,应注意把参数名与数据库表中的字段区别开来,否则将初见无法预期的结果
可以,在后面加上character set gbk
create procedure userInfo (IN u_name VARCHAR(50) character set gbk , out u_age int)