12.Mysql存储过程和函数

时间:2022-12-24 20:38:49

12.存储过程和函数
12.1 什么是存储过程和函数
存储过程和函数是事先经过编译并存储在数据库中的一段SQL语句的集合,
调用存储过程和函数简化应用开发人员的工作,减少数据在数据库和应用服务器之间的传输,有利于提高数据的处理效率。
存储过程和函数的区别:
函数必须有返回值,而存储过程没有;
存储过程的参数类型可以是IN、OUT、INOUT,而函数的参数类型只能是IN;
函数一般通过select语句调用,存储过程通过call命令调用;
函数中只能进行select操作,存储过程中可进行select\insert\update\delete操作。

12.2 存储过程和函数的相关操作
create routine 创建存储过程和函数的权限
alter routine 修改或删除存储过程和函数的权限
execute 执行存储过程和函数的权限

12.2.1 创建、修改存储过程和函数
1.创建存储过程语法:
delemiter $$
create procedure proc_name([参数类型 参数名 数据类型[,...]])
[characteristic特征]
begin
过程体
end $$
delemiter ;
说明:
参数类型取值分为:输入参数IN、输出参数OUT、输入输出参数INOUT,默认值为IN
characteristic特征:
language sql 说明过程体使用SQL语言编写,以后可能会支持其它语言编写
| [not] deterministic 是否确定,deterministic确定指两次相同的输入结果肯定是一致的;not deterministic不确定指两次相同的输入结果可能是不一致的。
| {contains sql | no sql | reads sql data | modifies sql data} 对数据的处理方式,contains sql指不包含读写数据的语句,no sql指不包含sql语句,reads sql data指包含读不包含写数据的语句,modifies sql data指包含写数据的语句
| sql security {definer | invoker} 执行存储过程的角色,sql security definer指使用创建者权限执行存储过程,sql security invoker指使用调用者权限执行存储过程,默认sql security definer。
| comment 'string' 存储过程的注释信息
过程体:有效的存储过程语句,
允许包含DDL语句(create\alter\drop\truncate等)和DCL语句(commit\rollback),
不允许包括load data infile语句,允许调用其它存储过程和函数。
delemiter $$ 将语句结束符设置为$$,使存储过程和函数中的";"不被错误的认为是语句结束符。
delemiter ; 将语句结束符设置为;,将语句结束符更改回原来的符号。
存储过程目前还不支持create or replace语法,如果需要修改存储过程可使用alter或先drop再create。

2.修改存储过程语法:
alter procedure proc_name [characteristic特征];
characteristic特征:
| {contains sql | no sql | reads sql data | modifies sql data} 对数据的处理方式
| sql security {definer | invoker} 安全控制
| comment 'string' 说明、注释

3.创建函数语法:
create function func_name([参数名 数据类型[,...]]) returns 返回值类型
[characteristic特征]
begin
函数体
end$$
说明:
函数参数类型只能为IN,默认参数类型也是IN,所以不用写参数类型

4.修改函数语法:
alter function func_name [characteristic特征];
characteristic特征:
| {contains sql | no sql | reads sql data | modifies sql data} 对数据的处理方式
| sql security {definer | invoker} 安全控制
| comment 'string' 说明、注释

5.调用过程语法
call proc_name([参数类型 参数名 数据类型[,...]]);

6.调用函数
select proc_name([参数名 数据类型[,...]]);

12.2.2 删除存储过程和函数
删除存储过程语法:
drop procedure [if exists] proc_name;
删除函数语法:
drop function [if exists] func_name;
注意:
一次只能删除一个存储过程或函数;
删除存储过程或函数时要有对该存储过程或函数的alter routine权限。

12.2.3 查看存储过程和函数
查看存储过程的状态:
show procedure status like 'proc_name';
查看函数的状态:
show function status like 'func_name';
查看存储过程的定义:
show create procedure proc_name;
查看函数的定义:
show create function func_name;
通过数据字典查看更详细的存储过程和函数信息:
select * from information_schema.routines where routine_name='具体的存储过程或函数名';

12.2.4 变量的使用
1.定义变量
语法:
declare v_name[,...] datatype [default value];
说明:
变量不区分大小写;
变量为局部变量,作用域在Begin和end之间;
变量定义要写在其它语句之前;
可以一次定义多个相同类型的变量;
可以给变量用default赋默认值;
2.变量赋值
直接赋值
set v_name=expr[,v_name2=expr2]...
查询赋值
select col_name into v_name from table_name where condition limit 1;
说明:
变量可以赋值常量,也可以赋值表达式;
set语句可以一次给多个变量赋值;
查询赋值时查询结果必须限定为一行。

12.2.5 定义条件和处理
定义条件和处理可以用来定义在处理过程中遇到问题时相应的处理步骤。
1.定义条件
declare cond_name condition for sqlstate [Value] sqlstate_value | mysql_error_code;
2.定义处理(异常处理)
declare handler_type handler for condition_value[,...] sp_statement;
说明:
handler_type取值包括:即当遇到condition_value定义的异常时程序如何跳转
continue 继续执行下面的语句;
exit 执行终止,直接退出;
undo 执行终止,并回滚前面的语句,目前还不支持。
condition_value取值包括:
cond_name 条件中定义的条件名
mysql_error_code 报错编码
sqlstate [Value] sqlstate_value SQL状态值
sqlwarning 警告,以01开头的sqlstate代码
not found 未找到数据,以02开头的sqlstate代码
sqlexception 异常,没有被sqlwarning和not found捕获的sqlstate代码
程序没有定义异常处理时,如果程序执行过程中遇到异常时,则抛出异常并退出;
程序定义异常处理后,如果程序执行过程中遇到异常时,则按照定义的异常处理执行,不会将异常抛出,一般用于批量处理的程序中。

12.2.6 光标的使用
光标可以对结果集进行循环处理。
光标使用步骤分为:定义光标、打开光标、提取数据、关闭光标。
定义光标:
declare cursor_name cursor for select语句;
declare exit handler for not found colse cursor_name;
打开光标:
open cursor_name;
提取数据:
repeat
fetch cursor_name into var_name[,var_name2]...;
until 0 end repeat;
关闭光标:
close cursor_name;
说明:
声明顺序:变量--》条件--》光标--》异常处理;
每个存储过程中只能声明一个光标;
声明光标后必须紧跟着声明异常处理declare exit handler for not found colse cursor_name;
其它类型的异常处理类型不能和光标的异常处理类型重复,即不能包含not found;
在循环repeat中逐行提取数据,用多个变量逐个接收1行记录的多个列信息。

12.2.7 流程控制
流程控制包括:if,case,loop,leave,iterate,repeat,while等条件分支语句和循环语句。
1.IF条件语句语法
if expr_condition then
语句1;
[elseif expr_condition2 then
语句2;]...
[else
语句n;]
end if;
说明:
IF条件语句实现了一重分支,二重分支,多重分支;
根据各条件表达式的真假,有选择的执行语句1|语句2|...|语句n。
2.case条件语句语法:
等值比较:
case 表达式
when 表达式1 then 语句1;
[when 表达式2 then 语句2;]...
[else 语句n;]
end case;
说明:
当表达式与表达式1相等时执行语句1,
当表达式与表达式2相等时执行语句2,...一次类推
当表达式与上述表达式都不相等时执行语句n。
范围比较:
case
when 逻辑表达式1 then 语句1;
[when 逻辑表达式2 then 语句2;]...
[else 语句n;]
end case;
说明:
当逻辑表达式1为真时执行语句1,
当逻辑表达式2为真时执行语句2,...一次类推
当上述逻辑表达式都不为真时执行语句n。
3.loop循环语句语法
lable_name: loop
语句1;
end loop lable_name;
说明:
无限循环语句1;
一般情况下需要在Loop循环中增加循环退出的条件。
4.leave退出本层循环
lable_name: loop
语句1;
if expr_condition then
leave lable_name;
end if;
语句2;
end loop lable_name;
说明:
Loop循环在expr_condition为真时退出本层Loop循环。
5.iterate退出本层次循环,接着进行下次循环
lable_name: loop
语句1;
if expr_condition then
iterate lable_name;
end if;
语句2;
end loop lable_name;
说明:
Loop循环在expr_condition为真时退出本次Loop循环,即不执行本次的语句2,接接着语句1开始下次循环。
6.repeat条件控制循环
lable_name: repeat
语句1;
until search_condition
end repeat lable_name;
说明:
当满足search_condition条件时退出循环

7.while
lable_name: while search_condition do
语句1;
end while lable_name;
说明:
当满足search_condition条件时进入循环,不满足时退出循环

12.2.8 事件调度器
定义:按照自定义的时间周期特定的执行某种操作。
语法:
create event 事件名 on schedule 时间周期 do 某种操作;
说明:
create event子句指定事件名;
on schedule子句指定事件的执行时间和频次;
do子句指定要执行的具体操作。
例子1:
create event my_event1 on schedule at current_timestamp + interval 1 hour do update emp set sal=sal+1;
例子2:
创建测试表test
create table test(id1 varchar(10),create_time datetime);
创建事件调度器,每5秒插入一条数据
create event event_test1 on schedule every 5 second do insert into test values ('test',now());
查看事件调度器:
show events;
select * from information_schema.events;
查看事件调度器状态(系统参数):
show variables like '%scheduler%';
show variables like 'event_scheduler';
select * from information_schema.GLOBAL_VARIABLES where variable_name='EVENT_SCHEDULER';
select * from information_schema.SESSION_VARIABLES where variable_name='EVENT_SCHEDULER';
select * from information_schema.GLOBAL_STATUS;
Error Code 3167解决方法:
show variables like '%show_compatibility_56%';
set global show_compatibility_56=on;
select * from information_schema.SESSION_VARIABLES
select * from information_schema.GLOBAL_VARIABLES;
select * from information_schema.GLOBAL_STATUS;

启用事件调度器(修改系统参数):数据库重启后需要重新启用事件调度器
set global event_scheduler=1;
查看后台进程:发现多了一个调度进程
show processlist;
'4', 'event_scheduler', 'localhost', NULL, 'Daemon', '1', 'Waiting for next activation', NULL
在查看test表,发现已有数据插入,为了防止表中数据快速增长,创建新的事件调度器,每2分钟清空一次test表:
create event event_test2 on schedule every 2 minute do truncate table test;

禁用事件调度器:
alter event event_test2 disable;
删除事件调度器:
drop event event_test1;

时间周期:
every 5 second 每隔5秒
every 1 minute 每隔1分钟
at current_timestamp + interval 1 hour 首次执行为当前时间,后续每隔1小时执行
当前日期:select CURDATE();
当前日期+1天:select DATE_ADD(CURDATE(), INTERVAL 1 DAY);
当前日期+1天+1小时:select DATE_ADD(DATE_ADD(CURDATE(), INTERVAL 1 DAY), INTERVAL 1 HOUR);

ON SCHEDULE 计划任务,有两种设定计划任务的方式:
1. AT 时间戳,用来完成单次的计划任务。
2. EVERY 时间(单位)的数量时间单位[STARTS 时间戳] [ENDS时间戳],用来完成重复的计划任务。
在两种计划任务中,
时间戳可以是任意的TIMESTAMP 和DATETIME 数据类型, 时间戳需要大于当前时间。
在重复的计划任务中,时间(单位)的数量可以是任意非空(Not Null)的整数式,时间单位是关键词:YEAR,MONTH,DAY,HOUR,MINUTE 或者SECOND。
例子:
create event event_incremental on schedule at '2018-08-12 01:00:00' + interval 1 DAY do call zgai_db.proc_incremental();
类型:一次
执行时间:2018-08-13 01:00:00
create event event_incremental3 on schedule at '2018-08-12 01:00:00' + interval 1 HOUR do call zgai_db.proc_incremental();
类型:一次
执行时间:2018-08-12 02:00:00
create event event_incremental on schedule EVERY 1 DAY do call zgai_db.proc_incremental();
类型:循环
间隔:1天
起始时间:当前时间
CREATE EVENT IF NOT EXISTS event_incremental4
ON SCHEDULE EVERY 1 DAY STARTS DATE_ADD(DATE_ADD(CURDATE(), INTERVAL 1 DAY), INTERVAL 1 HOUR)
ON COMPLETION PRESERVE ENABLE
DO call zgai_db.proc_incremental();
类型:循环
间隔:1天
起始时间:明天1点

查看event是否开启: show variables like '%sche%';
将事件计划开启: set global event_scheduler=1;
关闭事件任务: alter event e_test ON COMPLETION PRESERVE DISABLE;
开户事件任务: alter event e_test ON COMPLETION PRESERVE ENABLE;
12.3 小结

12.4 mysql动态sql示例
预制语句的SQL语法基于三个SQL语句:
PREPARE stmt_name FROM preparable_stmt;
EXECUTE stmt_name [USING @var_name [, @var_name] ...];
{DEALLOCATE | DROP} PREPARE stmt_name;

查询某个表的行数
USE `zgai_db`;
DROP PROCEDURE IF EXISTS `proc_dynamic_test`;
delimiter $$
USE `zgai_db`$$
CREATE PROCEDURE proc_dynamic_test(IN table_name varchar(255))
begin
declare var_sql varchar(800);
set var_sql = concat_ws(' ', 'select count(*) from ', table_name,' where 1=1');
set @sql = var_sql;
PREPARE s1 from @sql;
EXECUTE s1;
deallocate prepare s1;
end$$
delimiter ;

call proc_dynamic_test('AI_const');