mysql存储过程和函数

mysql存储过程和函数

1. 创建存储过程

创建格式:

create procedure sp_name ([proc_parameter])
    [characteristic...] routine_body
  • create procedure 用来创建存储过程的关键字, sp_name 为存储过程的名字, proc_parameter 为存储过程的参数列表, characteristic 为存储过程的特性, routine_body 存储过程的sql代码内容.

  • proc_parameter 参数列表格式:

    in表示入参, out表示出参, inout 表示既可以输入也可以输出; type为参数类型.

    [in | out | inout] param_name type

  • characteristic 指定存储过程的特征, 可以有如下取值:

characteristic的取值 说明
language sql 表示routine_body是由sql语句组成
[not] deterministic deterministic 表示,结果确定(相同输入获得相同的输出); 默认 not deterministic, 表示结果不确定
{contains sql | no sql | reads sql data| modifies sql data} 表示子程序包含sql语句, 但不包含读写语句; 2. 不包含sql语句; 3. 包含写; 4. 包含读. 默认 contains sql
sql security {definer | invoker} 指明谁有权限来执行, definer 只有定义者才能执行; invoker 有权限的调用者可执行; 默认 definer
comment ‘string’ 注释信息, 用来描述存储过程或函数
  • routine_body 为sql代码内容, 以begin开始, end结束. 例如:

    CREATE PROCEDURE getStuById(IN stuId INT)
    BEGIN select * from r where no = stuId;
    end;
    CALL getStuById(25); -- call 调用存储过程

2. 创建存储函数

创建格式:

create function func_name([func_parameter]) 
    returns type
    [characteristic...] routine_body
  • returs type 表示函数返回的数据类型. 其他参数与创建存储过程的形同.
CREATE FUNCTION getStuName () 
    RETURNS char (10) 
    RETURN (SELECT NAME FROM r WHERE r.NO = 25 );
SELECT getStuName () as name; -- 调用存储函数

注意: function 中的参数默认为in 参数。returns 字句只能对function做指定, 对于function而言是强制的, 并且函数体中必须包含一个return value语句。

3. 变量的使用

变量可以在子程序中声明并使用, 这些变量的作用范围是在begin…end程序中.
1. 定义变量
格式: var_name 局部变量名称, default value为变量提供一个默认值, 默认值可以为常量也可以为表达式, 没有default字句, 初始值为null

declare var_name[, varname]... date_type[default value];
-- 例如: declare myparam int default 10;
  1. 变量赋值
    方式一: 使用set,为一个变量赋值:
set var_name = expr[, var_name=expr]...;

方式二: 使用select…into, 为一个或者多个变量赋值

select col_name[,...] into var_name[,...] table_expr;

例如:

declare var1, var2 int; 
set var1 = 10, var2=20;  -- 方式一
set var2=var1+var2;
select id, num into var1, var2 from item where item.name="图书"; -- 方式二

4. 定义条件和处理程序

特定条件需要特定处理。定义条件是事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时,应当采取的处理方式, 保证存储过程和函数在遇到警告和错误时能继续执行。这样可以增强存储程序处理问题的能力, 避免程序异常停止运行。
1. 定义条件
格式如下, 其中, sqlstate_value为长度为5的字符串类型错误代码,mysql_error_code为数值类型错误代码。 例如: ERROR 1142(42000) 中, sqlstate_value值为42000, mysql_error_code的值为1142。

  declare condition_name condition for [condition_type]

  [condition_type]: 
  sqlstate [value] sqlstate_value | mysql_error_code   

  -- 以下为例子: 定义ERROR 1148(42000) 错误, 名称为command_not_allowed

  //方式一: 使用sqlstate_value
  declare command_not_allowed condition for sqlstate_valuestate '42000'
  //方式二: 使用mysql_error_code
  declare command_not_allowed condition for 1148

定义条件, 将condition_name与指定的错误类型关联起来. condition_name可以随后被用在定义处理程序的declare handler 语句中.

  1. 定义处理程序
    格式如下:
declare handler_type handler for condition_value[,...] sp_statement handler_type: continue | exit | undo
  • handler_type 为错误处理方式, 有三个值, continue表示遇到错误继续执行,不处理; exit遇到错误退出; undo遇到错误撤回之前的操作, mysql中暂时不支持这样的操作.默认exit.
  • condition_value 表示错误类型, 可以有以下取值:
condition_value 说明
sqlstate[value] sqlstate_value 包含5个字符的字符串错误值
condition_name declare condition定义的错误条件名称
sqlwarning 匹配所有以01开头的sqlstate错误代码
not found 匹配所有以02开头的sqlstate错误代码
sqlexception 匹配所有没有被sqlwarning或者not found捕获的sqlstate错误代码
mysql_error_code 匹配数值类型错误代码

- sql_statement 参数为程序语句段, 表示在遇到定义的错误时, 需要执行的存储过程或函数.

6种处理程序的方法:

// 方法一: 捕获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';
//方法五: 使用sqlexception
declare exit handler for sqlexception set @info='error';
// 方法六: 使用not found
declare exit handler for not found set @info='NO_SUCH_TABLE'

select @info; -- 打印用户变量info的值

注意:
@var_name 表示用户变量, 使用set语句为其赋值, 用户变量与连接有关, 一个客户端定义的用户变量不能被其他客户端使用或看到; 当客户端退出时, 改客户端连接的所有变量将自动释放.

5. 光标的使用

查询语句可能返回多条记录, 如果数据量很大, 需要在存储过程中和存储函数中使用光标来逐条读取结果集中的记录。

  1. 声明光标

    光标必须在声明处理程序之前被声明, 并且变量和条件还必须在声明光标或处理程序之前被声明.

    格式如下, 其中, cursor_name表示光标名称, select_statement参数表示select语句的内容, 返回一个用于创建光标的结果集.

    declare cursor_name cursor for select_statement
    -- 例如:  declare cursor_fruit cursor for select name, price from fruit;
  2. 打开关闭光标
open cursor_name; -- 打开光标, 例如: open cursor_fruit;
close cursor_name; -- 关闭光标, 例如: close cursor_fruit; 若未明确地关闭, 光标在它被声明的复合语句的末尾被关闭.
  1. 使用光标
    打开光标后使用.格式如下:
-- var_name 表示将光标中select语句中查询出的信息存入改参数中, var_name必须在声明光标之前就定义好.
fetch cursor_name into var_name [, var_name]...  
-- 例如: fetch cursor_fruit into fruit_name, fruit_price; 

### 6. 流程控制

流程控制语句用来根据条件控制语句的执行, mysql中用来构造控制流程语句有: if语句, case语句, loop语句, while语句, leave语句, iterate语句, repeat语句.
每个流程中可能包含一个单独语句, 或者是使用begin…end构造的复合语句, 构造可以被嵌套.

  1. if语句
    类似java中的if
if expr_condition then statement_list
    [elseif expr_condition then statement_list]...
    [else statement_list]
end if
-- 以下为例子:
if val is null
    then select 'val is null';
    else select 'val is not null';
end if;
  1. case语句
    类似java中的switch 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 expr_condition then statement_list
    [when expr_condition then statement_list]...
    [else statement_list]
end case
-- 例子:
case 
    when val is null 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;
  1. leave语句
    用来退出任何被标注的流程控制构造, 类似java语言中循环语句中使用return退出循环。
  2. iterate语句
    将执行顺序转到语句段开头处, 类似java语言中循环语句中的 continue

  3. loop语句
    loop用来重复执行语句, 需要使用leave语句退出循环; loop_label表示loop语句的标注名称, 可以省略, statement_list需要循环执行的语句

[loop_label:] loop
    statement_list
end loop [loop_label]

结合leave, iterate, loop示例:

create procedure doiterate()
BEGIN
declare p1 int default 0;
my_loop: loop
    set p1 = p1 + 1;
    if p1 < 10 then iterate my_loop;  -- p1小于10, 跳到下一次循环起始处
    elseif p1 > 20 then leave my_loop;-- p1 大于20, 退出循环
    end if;
    select 'p1 is between 10 and 20';
end loop my_loop;
end
  1. repeat语句

​ 类似do…while语句, 格式如下:

[repeat_label:] repeat 
    statement_list
util expr_condition
end repeat [repeat_label]

-- 示例:
declare id int default 0;
repeat
set id = id + 1;
until id >= 10    -- id大于等于10, 退出循环
end repeat;
  1. while语句
    类似java中的while循环, 格式如下:
[while_label:] while expr_condition do
    statement_list
end while [while_label]

-- 例子:
declare i int default 0;
while i < 10 do
set i = i + 1;
end while;

7. 查看存储过程和函数

用户可以通过show status 语句或者 show create 语句 或者直接从系统的information_schema数据库中查询。
1. 使用show status

show {procedure | function} status [like 'param']

返回子程序的特征, 如数据库、名字、类型、创建者及创建和修改的日期。 如果没有指定样式, 则查出所有的存储函数或者过程信息。procedure和function分别表示存储过程和函数。例子:

show procedure  status like 'C%'\G  -- 查看C开头的存储过程信息
show function status like 'C%'\G  -- 查看C开头的存储函数信息
  1. 使用show create
show create {procedure | function} sp_name

-- 示例, 查看数据库db_course下的存储过程avgFruitPrice: 
mysql> show create PROCEDURE db_course.avgFruitPrice\G;
*************************** 1. row ***************************
           Procedure: avgFruitPrice
            sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
    Create Procedure: CREATE DEFINER=`mini1`@`%` PROCEDURE `avgFruitPrice`()
BEGIN
    SELECT
        avg(NO) AS avgNo
    FROM
        r;
END
character_set_client: utf8
collation_connection: utf8_general_ci
  Database Collation: utf8_general_ci
1 row in set (0.00 sec)

ERROR: 
No query specified
  1. 从information_schema.Routines表中查看存储过程和函数信息

    information_schema数据库下ROUTINES表存储所有存储过程和函数的定义。ROUTINE_NAME存储过程的名字或者存储函数的名字。ROUTINE_TYPE 值为function 或者procedure

    SELECT * from information_schema.ROUTINES WHERE ROUTINE_NAME='sp_name';
    
    -- 例如: SELECT * from information_schema.ROUTINES WHERE ROUTINE_NAME='avgFruitPrice';

8. 修改存储过程和函数特征

修改存储过程或者函数的特征, 例如注释信息, 子程序是否能包含读写数据的语句,等. characteristic的取值文章前半部分已给出。

alter {procedure | function} sp_name [characteristic ...]

例子: 修改avgFruitPrice定义, 将读写权限改为modifes sql data, 并指明调用者可以执行。

alter procedure avgFruitPrice 
modifes sql data 
sql security invoker

9. 删除存储过程或者函数

drop {procedure | function} {if exists} sp_name;

-- 例如: drop producer avgFruitPrice;

if exists 可以防止删除一个不存在的存储函数或者过程时, 发生错误。

10. 需要注意的问题

  1. mysql 存储过程与函数的区别

本质上都属于存储程序。区别:
- 函数只能通过return语句返回单个值或者表对象; 而存储过程不允许执行return, 但是可以通过out参数返回多个值.
- 函数限制比较多, 不能用临时表, 只能用表变量, 还有一些函数都不可用等; 存储过程的限制相对较少.
- 函数可以嵌套在sql语句中使用, 可以在select语句中作为查询语句的一部分调用; 存储过程一般是作为一个独立部分使用.

  1. 存储过程中的代码修改问题
    目前, mysql 还不提供对已存在的存储过程代码的修改, 如果要修改存储过程, 必须使用drop语句删除之前的,然后重新创建一个新的存储过程.

  2. 存储过程调用其他存储过程
    存储过程包含用户定义的sql语句集合, 可以使用call语句调用存储过程, 可以在存储过程中使用call代用其他存储过程, 但是不能使用drop删除其他存储过程.

  3. 存储过程的参数与数据库表字段
    在定义存储过程参数时, 应该把存储过程变量与数据库表字段区分开来, 否则可能会发生不可预知的错误.

  4. 存储过程参数是否可以为中文
    一般情况下, 可能会出现存储过程中需要传入中文参数的情况, 例如某个存储过程根据用户名字查找该用户. 这是需要在定义存储过程时, 在后面加上character set gbk, 不然调用存储过程使用中文参数会出错, 比如userInfo存储过程,代码如下:

create procedure userInfo(in u_name varchar(50) character set gbk, out u_age int) 

本笔记内容参考《mysql5.6从零开始学》一书,仅作为学习笔记

你可能感兴趣的:(mysql)