创建格式:
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 调用存储过程
创建格式:
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语句。
变量可以在子程序中声明并使用, 这些变量的作用范围是在begin…end程序中.
1. 定义变量
格式: var_name 局部变量名称, default value为变量提供一个默认值, 默认值可以为常量也可以为表达式, 没有default字句, 初始值为null
declare var_name[, varname]... date_type[default value];
-- 例如: declare myparam int default 10;
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="图书"; -- 方式二
特定条件需要特定处理。定义条件是事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时,应当采取的处理方式, 保证存储过程和函数在遇到警告和错误时能继续执行。这样可以增强存储程序处理问题的能力, 避免程序异常停止运行。
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 语句中.
declare handler_type handler for condition_value[,...] sp_statement handler_type: continue | exit | undo
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语句为其赋值, 用户变量与连接有关, 一个客户端定义的用户变量不能被其他客户端使用或看到; 当客户端退出时, 改客户端连接的所有变量将自动释放.
查询语句可能返回多条记录, 如果数据量很大, 需要在存储过程中和存储函数中使用光标来逐条读取结果集中的记录。
声明光标
光标必须在声明处理程序之前被声明, 并且变量和条件还必须在声明光标或处理程序之前被声明.
格式如下, 其中, cursor_name表示光标名称, select_statement参数表示select语句的内容, 返回一个用于创建光标的结果集.
declare cursor_name cursor for select_statement
-- 例如: declare cursor_fruit cursor for select name, price from fruit;
open cursor_name; -- 打开光标, 例如: open cursor_fruit;
close cursor_name; -- 关闭光标, 例如: close cursor_fruit; 若未明确地关闭, 光标在它被声明的复合语句的末尾被关闭.
-- 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构造的复合语句, 构造可以被嵌套.
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;
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;
iterate语句
将执行顺序转到语句段开头处, 类似java语言中循环语句中的 continue
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
类似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;
[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;
用户可以通过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开头的存储函数信息
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
从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';
修改存储过程或者函数的特征, 例如注释信息, 子程序是否能包含读写数据的语句,等. characteristic的取值文章前半部分已给出。
alter {procedure | function} sp_name [characteristic ...]
例子: 修改avgFruitPrice定义, 将读写权限改为modifes sql data, 并指明调用者可以执行。
alter procedure avgFruitPrice
modifes sql data
sql security invoker
drop {procedure | function} {if exists} sp_name;
-- 例如: drop producer avgFruitPrice;
if exists 可以防止删除一个不存在的存储函数或者过程时, 发生错误。
本质上都属于存储程序。区别:
- 函数只能通过return语句返回单个值或者表对象; 而存储过程不允许执行return, 但是可以通过out参数返回多个值.
- 函数限制比较多, 不能用临时表, 只能用表变量, 还有一些函数都不可用等; 存储过程的限制相对较少.
- 函数可以嵌套在sql语句中使用, 可以在select语句中作为查询语句的一部分调用; 存储过程一般是作为一个独立部分使用.
存储过程中的代码修改问题
目前, mysql 还不提供对已存在的存储过程代码的修改, 如果要修改存储过程, 必须使用drop语句删除之前的,然后重新创建一个新的存储过程.
存储过程调用其他存储过程
存储过程包含用户定义的sql语句集合, 可以使用call语句调用存储过程, 可以在存储过程中使用call代用其他存储过程, 但是不能使用drop删除其他存储过程.
存储过程的参数与数据库表字段
在定义存储过程参数时, 应该把存储过程变量与数据库表字段区分开来, 否则可能会发生不可预知的错误.
存储过程参数是否可以为中文
一般情况下, 可能会出现存储过程中需要传入中文参数的情况, 例如某个存储过程根据用户名字查找该用户. 这是需要在定义存储过程时, 在后面加上character set gbk, 不然调用存储过程使用中文参数会出错, 比如userInfo存储过程,代码如下:
create procedure userInfo(in u_name varchar(50) character set gbk, out u_age int)
本笔记内容参考《mysql5.6从零开始学》一书,仅作为学习笔记