07-MySQL存储过程与函数

07-MySQL存储过程与函数


  • 07-MySQL存储过程与函数
    • 1 创建存储过程和函数
      • 1.1 创建存储过程
      • 1.2 创建存储函数
      • 1.3 变量的使用
      • 1.4 定义条件和处理程序
      • 1.5 流程控制语句
        • 1.5.1 IF语句
        • 1.5.2 CASE 语句
        • 1.5.3 LOOP语句
        • 1.5.4 LEAVE语句
        • 1.5.5 ITERATE语句
        • 1.5.6 REPEAT 语句
        • 1.5.7 WHILE 语句
    • 2 调用存储过程
      • 2.1 调用存储过程
      • 2.2 调用存储函数
      • 2.3 查看存储过程和函数
    • 3 修改存储过程和函数
    • 4 删除存储过程和函数
    • 5 Q&A
        • 5.1 MySQL存储过程和函数有什么区别
        • 5.2 存储过程中的代码可以改变吗?
        • 5.3 存储过程可以调用其他存储过程吗?
        • 5.4 存储过程命名
        • 5.5 存储过程的参数可以使用中文吗


1 创建存储过程和函数

​ 存储过程就是一条或者多条SQL语句的集合。

1.1 创建存储过程

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;

1.2 创建存储函数

语法:

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");

要注意的是:

  1. 如果在存储函数中的return 语句返回一个类型不同于函数的returns 子句中指定类型的值,返回值将被将至为恰当的类型。如一个函数返回一个ENUM或者SET值,但是return 语句返回一个整数,对于set成员集相应的enum成员,从函数返回的值是字符串。
  2. 指定参数为IN、OUT或者INOUT只对 procedure 存储过程是合法的,function 函数中总是默认为IN参数。Returns 子句只能对function做指定,对函数而言这是强制的。它用来指定函数的返回类型,而且函数体必须包含一个returen value语句。

1.3 变量的使用

变量作用范围是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';

1.4 定义条件和处理程序

定义条件:实现定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时应该采取的处理方式,并且保证存储过程或函数在遇到警告或者错误时能够继续执行。这样可以增强存储过程处理问题的能力,避免程序异常停止运行。

定义条件使用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语句为其赋值,用户变量与连接有关,一个客户端定义的变量不能被其他客户端看到或者使用。当客户端退出时,该客户链接的所有变量将被释放。

1.5 流程控制语句

1.5.1 IF语句

​ 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;

1.5.2 CASE 语句

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;

1.5.3 LOOP语句

​ 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;

1.5.4 LEAVE语句

​ leave语句用来退出任何被标注的流程控制结构,leave语句格式如下:

leave label 

例子:

add_num : LOOP
SET @count = @count+1;
if @count=50 then leave add_num;
end loop add_num;

1.5.5 ITERATE语句

​ 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

1.5.6 REPEAT 语句

​ 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;

1.5.7 WHILE 语句

​ 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;

2 调用存储过程

​ 存储过程和函数有多种调用方式,窜出过程必须使用CALL语句调用,并且存储过程和数据库相关,如果执行其他数据库中的存储过程,需要制定数据库名称。例如 CALL dbname.procname

2.1 调用存储过程

例子:

--  创建存储过程
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;

2.2 调用存储函数

例子:

-- 调用存储函数
create function countProc2 (sid int)
    returns int 
begin
    return (select count(*) from fruits where s_id = sid);
end;

-- 调用存储函数
select CountProc2(101);

2.3 查看存储过程和函数

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)

3 修改存储过程和函数

语法:

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';

4 删除存储过程和函数

语法:

drop { procedure | function }[if exists] sp_name

例子:

drop procedure CountProc;
drop function CountProc;

5 Q&A

5.1 MySQL存储过程和函数有什么区别

​ 本质上他们都是存储程序。

  1. 函数只能通过return语句返回单个值或者表对象,而存储过程不允许执行return 语句,但是可以通过out参数返回多个值。
  2. 函数限制比较多,不能使用临时表,只能用表变量。存储过程限制少。
  3. 函数可以潜入在SQL中使用,可以在SELECT语句中作为查询语句的一个部分调用;而存储过程一般作为一个独立的部分来执行。

5.2 存储过程中的代码可以改变吗?

​ 不能,drop掉重新创建。

5.3 存储过程可以调用其他存储过程吗?

​ 可以,使用CALL语句调用其他存储过程,但是不能DROP。

5.4 存储过程命名

​ 在定义存储过程参数列表时,应注意把参数名与数据库表中的字段区别开来,否则将初见无法预期的结果

5.5 存储过程的参数可以使用中文吗

​ 可以,在后面加上character set gbk

create procedure userInfo (IN u_name VARCHAR(50) character set gbk , out u_age int)

你可能感兴趣的:(MySQL)