存储的例程是一组可以存储在服务器中的SQL语句。完成此操作后,客户无需继续重新发出各个语句,而是可以引用存储的例程。执行存储过程可以获取存储过程的结果集
存储的例程在某些情况下特别有用:
- 当多个客户端应用程序以不同的语言编写或在不同的平台上工作时,但需要执行相同的数据库操作。
- 当安全至上时。例如,银行将存储过程和函数用于所有常见操作。这提供了一致且安全的环境,例程可以确保正确记录每个操作。在这种设置中,应用程序和用户将无法直接访问数据库表,而只能执行特定的存储例程。
存储的例程可以提高性能,因为在服务器和客户端之间需要发送的信息较少。折衷方案是,这确实会增加数据库服务器的负载,因为更多的工作在服务器端完成,而较少的工作在客户端(应用程序)端完成。如果仅一台或几台数据库服务器为许多客户端计算机(例如Web服务器)提供服务,则应考虑这一点。
存储的例程还能够在数据库服务器中具有函数库。
存储的例程可以是过程,也可以是函数
当调用例程时,将执行隐式use db_name(当例程终止时将执行撤消)。不允许在存储例程中使用语句。
可以使用数据库名称限定例程名称。这可以用来引用不在当前数据库中的例程。例如,要调用与test数据库关联的存储过程p或函数f,可以调用test.p()或test.f()。
当一个数据库被删除时,所有与它相关的存储例程也被删除。
存储的函数不能递归
允许在存储过程中进行递归,但默认情况下禁用。要启用递归,请将max_sp_recursion_depth系统变量设置为大于零的值。存储过程递归增加了对线程堆栈空间的需求。如果增加max_sp_recursion_depth的值,则可能有必要通过增加服务器启动时thread_stack的值来增加线程堆栈的大小 。
权限
创建存储例程需要CREATE ROUTINE权限。
修改或删除存储例程需要ALTER ROUTINE特权。如果有必要,这个特权会自动授予例程的创建者,并在例程被删除时从创建者处删除。
执行存储例程需要EXECUTE特权。但是,如果有必要,这个特权会自动授予例程的创建者(并在例程被删除时从创建者处删除)。此外,例程的默认SQL安全性特征是DEFINER,它允许访问与例程关联的数据库的用户执行例程。
如果automatic_sp_privileges系统变量为0,则不会自动向例程创建者授予EXECUTE和ALTER例程特权,也不会从例程创建者中删除这些特权。
例程的创建者是用于为其执行CREATE语句的帐户。这可能与例程中定义为定义器的帐户不同。
存储过程
//存储过程
CREATE
[DEFINER = user]
PROCEDURE sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body
proc_parameter:
[ IN | OUT | INOUT ] param_name type
type:
Any valid MySQL data type
characteristic: {
COMMENT 'string'
| LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
}
//存储函数
CREATE
[DEFINER = user]
FUNCTION sp_name ([func_parameter[,...]])
RETURNS type
[characteristic ...] routine_body
func_parameter:
param_name type
type:
Any valid MySQL data type
routine_body:
Valid SQL routine statement
括号内的参数列表必须始终存在。如果没有参数,()则应使用的空参数列表 。参数名称不区分大小写。
每个参数默认情况下为IN参数。要为参数另外指定,请使用关键字OUT或INOUT在参数名称之前
一个IN参数传递给存储过程,该过程可能会修改该值,但是该过程返回时,调用者看不到该修改。一个OUT参数是返回给调用者的值,它的初始值是NULL,存储过程返回时,调用者可以看到其值 。一个 INOUT参数是由调用者者初始化,可以由程序进行修改并返回给调用者
对于每一个OUT或INOUT 参数,CALL 调用程序时传递一个用户定义的变量,这样就可以得到存储过程返回的值。如果要从另一个存储过程或函数中调用过程,则还可以将例程参数或本地例程变量作为OUT或INOUT参数传递。
MySQL允许存储过程包含DDL语句,例如 CREATE和DROP。MySQL还允许存储过程(但不允许存储函数)包含SQL事务语句,例如 COMMIT。存储过程可能不包含执行显式或隐式提交或回滚的语句,返回结果集的语句可以在存储过程中使用,但不能在存储函数中使用。存储过程中的USE语句不允许使用
定义存储过程:
delimiter // #将sql终止符更改为//
create procedure pl() # 创建一个名为pl的存储过程
BEGIN #开始创建存储过程
sql 语句
END // #结束创建存储过程
delimiter ;
执行存储过程: call pl()
存储过程修改比较麻烦,建议直接删除原来的存储过程,然后在创建新的存储过程
存储过程需要给参数定义类型,而且当传值时必须对应指定的类型
存储过程的参数: 参数只能设定基础类型
1、in 表示将参数传给存储过程内部使用
2、out 表示将参数返回(类似 return),传入的参数的值不会传入到存储过程内部,call 调用时传入的参数必须为 @参数名(mysql定义变量方法),这样的存储过程执行后 select @参数名 即可获取值 out的参数可以不传值
3、inout 表示将参数传给存储过程内部使用,并返回这个参数 (可以传值 或者变量)
存储过程中赋值:
declare 用于定义变量
set 用于设置变量的值例:
delimiter //
create procedure p1(
in arg1 int,
out arg2 varchar(50),
inout arg3 int
)
begin
declare v int;
declare g int default 9;
set v = 1;
arg2 = 'eric';
arg3 = arg3 + 666;
update table set name = 'what' where id = 1;
select * from table;
end //
delimiter ;
@i2 = 123;
call p1(1,@i1,@i2)
select @i1,@i2 --》输出为:eric,789
//存储过程可以获取多种数据:数据值和结果集 (数据一般用来判断执行是否成功,比如0代表失败,1代表成功) 存储过程只能有一个结果集(也就意味着只能有一个select语句)
存储过程动态执行sql语句:可以有效的防止sql注入
delimiter \\
create procedure p4 (
in nid int
)
begin
set nid = 11;
set @nid = nid;
PREPARE prod FROM 'select * from student where sid > ?'; // ?时mysql中的占位符 创建一个变量 prod 代表后面的sql语句字符串
EXECUTE prod USING @nid; //字符串格式化 ,将nid拼接到sql语句中的占位符处, msyql要求拼接的变量前面必须带@
DEALLOCATE prepare prod; //执行sql语句
end \\
delimiter ;
查询存储过程
方法1:select name from mysql.proc where db='数据库名';
方法2:select routine_name from information_schema.routines where routine_schema='数据库名';
方法3:show procedure status where db='数据库名';
查看详细信息:show create procedure 数据库.存储过程名
删除存储过程
DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name
调用存储过程
CALL sp_name([parameter[,...]])
CALL可以使用OUT或INOUT参数将值返回给调用者。当过程返回时,客户端程序还可以获得在例程中执行的最终语句所影响的行数;
要使用OUT或INOUT参数从过程中获取值,需要传递自定义变量作为参数,然后在过程返回后检查变量的值。
如果要从另一个存储过程或函数中调用过程,则还可以将例程参数或本地例程变量作为IN或INOUT、INOUT参数传递。
查看例程定义
SHOW CREATE PROCEDURE
SHOW CREATE FUNCTION
查看例程内部实现的表示
SHOW PROCEDURE CODE proc_name
SHOW FUNCTION CODE func_name
返回存储过程的特征,例如数据库,名称,类型,创建者,创建和修改日期以及字符集信息
SHOW PROCEDURE STATUS [LIKE 'pattern' | WHERE expr]
SHOW FUNCTION STATUS [LIKE 'pattern' | WHERE expr]
存储例程的元数据
SELECT * from INFORMATION_SCHEMA.ROUTINES
示例:
清空数据库中所有的表的数据,并将表的自增值改为1
delimiter \\
CREATE PROCEDURE clear_all ( IN database_name VARCHAR ( 50 ) )
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE table_name1 VARCHAR ( 100 );
-- 定义游标,并将结果集赋值到游标中
DECLARE all_table CURSOR FOR SELECT table_name FROM information_schema.TABLES WHERE table_schema = database_name AND table_type = 'base table';
-- 声明当游标遍历完后将标志变量置成某个值
DECLARE CONTINUE HANDLER FOR NOT found SET done = 1;
-- 打开游标
OPEN all_table;
-- 将游标中的值赋值给变量,注意:变量名不要和返回的列名同名,变量顺序要和sql结果列的顺序一致
FETCH all_table INTO table_name1;
WHILE done <> 1 DO
-- 预定义sql,因为是动态的选择表名,所以表名并不能用 ? 来当占位符,只能采用字符串拼接的方法。
SET @sql1 = concat( 'truncate ', table_name1, ';' );
PREPARE truncate_sql FROM @sql1;
EXECUTE truncate_sql;
DEALLOCATE PREPARE truncate_sql;
-- 将游标中的值再赋值给变量,供下次循环使用
FETCH all_table INTO table_name1;
END WHILE;
CLOSE all_table;
END \\
delimiter;
删除数据库中所有表的数据,但是每个数据表保留10条数据
delimiter \\
CREATE PROCEDURE clear_all ( IN database_name VARCHAR ( 50 ) )
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE table_name1 VARCHAR ( 100 );
DECLARE all_table CURSOR FOR SELECT table_name FROM information_schema.TABLES WHERE table_schema = database_name AND table_type = 'base table';
DECLARE CONTINUE HANDLER FOR NOT found SET done = 1;
-- 打开游标
OPEN all_table;
-- 将游标中的值赋值给变量,注意:变量名不要和返回的列名同名,变量顺序要和sql结果列的顺序一致
FETCH all_table INTO table_name1;
WHILE done <> 1 DO
SET @sql1 = concat( 'select count(*) into @limit1 from ', table_name1);
PREPARE truncate_sql FROM @sql1;
EXECUTE truncate_sql;
DEALLOCATE PREPARE truncate_sql;
SET @limit1 = @limit1 - 10;
IF @limit1 >= 1 THEN
-- 预定义sql,因为是动态的选择表名,所以表名并不能用 ? 来当占位符,只能采用字符串拼接的方法。
SET @sql1 = concat( 'delete from ', table_name1, ' limit ', @limit1 );
PREPARE truncate_sql FROM @sql1;
EXECUTE truncate_sql;
DEALLOCATE PREPARE truncate_sql;
END IF;
-- 将游标中的值再赋值给变量,供下次循环使用
FETCH all_table INTO table_name1;
END WHILE;
CLOSE all_table;
END \\
delimiter;