概念
存储过程是一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
存储过程和函数是事先经过编译并存储在数据库中的一段SQL语句的集合。
存储过程是数据库中的一个重要对象。
存储过程和函数的区别:
• 函数必须有返回值,而存储过程没有。
• 存储过程的参数可以是IN、OUT、INOUT类型,函数的参数只能是IN
优缺点
优点:
1、重复使用:存储过程可以重复使用,从而可以减少数据库开发人员的工作量。
2、减少网络流量:存储过程位于服务器上,调用的时候只需要传递存储过程的名称以及参数就可以了,因此降低了网络传输的数据量。
3、安全性:参数化的存储过程可以防止SQL注入式攻击,而且可以将Grant、Deny以及Revoke权限应用于存储过程。
4、存储过程只在创建时进行编译;而SQL语句每执行一次就编译一次,所以使用存储过程可以提高 数据库执行速度。
5、 简化复杂操作,结合事务一起封装。
缺点:
1、调试麻烦,但是用 PL/SQL Developer 调试很方便!弥补这个缺点。
2、移植问题,数据库端代码当然是与数据库相关的。但是如果是做工程型项目,基本不存在移植问题。
3、重新编译问题,因为后端代码是运行前编译的,如果带有引用关系的对象发生改变时,受影响的存储过程、包将需要重新编译(不过也可以设置成运行时刻自动编译)。
4、如果在一个程序系统中大量的使用存储过程,到程序交付使用的时候随着用户需求的增加会导致数据结构的变化,接着就是系统的相关问题了,最后如果用户想维护该系统可以说是很难很难、而且代价是空前的,维护起来更麻烦。
适用场景:并发量高的情况下,为了提高效率,用存储过程比较多。
存储过程的创建
格式语法
创建
delimiter $$
create procedure 过程名(参数列表)
begin
SQL语句
end $$
delimiter ;
存储过程的参数形式:[IN | OUT | INOUT]参数名 类型
IN 输入参数
OUT 输出参数
INOUT 输入输出参数
调用
call 存储过程名(实参列表)
查看
(1)SHOW PROCEDURE STATUS 查看存储过程的状态,
(2)SHOW CREATE PROCEDURE 过程名 查看存储过程的信息
(3)INFORMATION_SCHEMA.ROUTINES 查看存储过程的信息
删除
DROP PROCEDURE 存储过程名
实例
例一参数为空
1、创建表
mysql> create table t1(
-> id int,
-> name varchar(50)
-> );
2、创建存储过程
mysql> \d $$
mysql> create procedure p_insert_t1()
-> begin
-> declare i int default 1;
-> while (i<20000) do
-> insert into t1 values(i,md5(i));
-> set i=i+1;
-> end while;
-> end $$
mysql> \d ;
3、调用存储过程
mysql> call p_insert_t1();
Query OK, 1 row affected (14.35 sec)
4、调用完成后查看数据
mysql> select count(1) from t1;
+----------+
| count(1) |
+----------+
| 19999 |
+----------+
5、查看存储过程信息
mysql> show create procedure p_insert_t1\G
*************************** 1. row ***************************
Procedure: p_insert_t1
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=`root`@`localhost` PROCEDURE `p_insert_t1`()
begin declare i int default 1; while (i<20000) do insert into t1 values(i,md5(i)); set i=i+1; end while; end
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8_general_ci
1 row in set (0.00 sec)
6、删除存储过程
例二参数为in
1、清除t1表中的数据
mysql> truncate table t1;
2、创建存储过程
mysql> \d $$
mysql> create procedure p2_insert_t1(in p1 int)
-> begin
-> declare i int default 1;
-> while (i<p1) do
-> insert into t1 values(i,md5(i));
-> set i=i+1;
-> end while;
-> end $$
mysql> \d ;
3、调用存储过程
mysql> call p2_insert_t1(50000);
Query OK, 1 row affected (37.25 sec)
4、测试
mysql> select count(1) from t1;
+----------+
| count(1) |
+----------+
| 49999 |
+----------+
例三参数为out
1、创建存储过程
mysql> \d $$
mysql> create procedure p3_count_t1(out p2 int)
-> begin
-> select count(1) into p2 from t1;
-> end $$
mysql> \d ;
2、查看当前变量值
mysql> select @p;
+------+
| @p |
+------+
| NULL |
+------+
3、调用存储过程
mysql> call p3_count_t1(@p);
Query OK, 1 row affected (0.02 sec)
4、测试结果
mysql> select @p;
+-------+
| @p |
+-------+
| 49999 |
+-------+
例四参数为in 和 out
mysql> select * from emp;
+-------+-----------+-----------+------+------------+-------+-------+--------+
| empno | ename | job | mgr | hiredate | sai | comm | deptno |
+-------+-----------+-----------+------+------------+-------+-------+--------+
| 1001 | 甘宁 | 文员 | 1013 | 2000-12-17 | 8000 | NULL | 20 |
| 1002 | 黛绮丝 | 销售员 | 1006 | 2001-02-20 | 16000 | 3000 | 30 |
| 1003 | 殷天正 | 销售员 | 1006 | 2001-02-22 | 12500 | 5000 | 30 |
| 1004 | 刘备 | 经理 | 1009 | 2001-04-02 | 29750 | NULL | 20 |
| 1005 | 谢逊 | 销售员 | 1006 | 2001-09-28 | 12500 | 14000 | 30 |
| 1006 | 关羽 | 经理 | 1009 | 2001-05-01 | 28500 | NULL | 30 |
| 1007 | 张飞 | 经理 | 1009 | 2001-09-01 | 24500 | NULL | 10 |
| 1008 | 诸葛亮 | 分析师 | 1004 | 2007-04-19 | 30000 | NULL | 20 |
| 1009 | 曾阿牛 | 董事长 | NULL | 2001-11-17 | 50000 | NULL | 10 |
| 1010 | 韦一笑 | 销售员 | 1006 | 2001-09-08 | 15000 | 0 | 30 |
| 1011 | 周泰 | 文员 | 1006 | 2007-05-23 | 11000 | NULL | 20 |
| 1012 | 程普 | 文员 | 1006 | 2001-12-03 | 9500 | NULL | 30 |
| 1013 | 庞统 | 分析师 | 1004 | 2001-12-03 | 30000 | NULL | 20 |
| 1014 | 黄盖 | 文员 | 1007 | 2002-01-23 | 13000 | NULL | 10 |
| 1015 | 张三 | 保洁员 | 1001 | 2013-05-01 | 80000 | 50000 | 50 |
+-------+-----------+-----------+------+------------+-------+-------+--------+
1、创建存储过程
mysql> \d $$
mysql> create procedure p4(in p1 varchar(255),in p2 varchar(255),out p3 int)
-> begin
-> select count(1) into p3 from emp where job=p1 and year(hiredate)=p2 ;
-> end$$
mysql> \d ;
2、调用存储过程
mysql> call p4('销售员',2001,@num);
Query OK, 1 row affected (0.10 sec)
3、测试结果
mysql> select @num;
+------+
| @num |
+------+
| 4 |
+------+
例五参数为inout
mysql> \d $$
mysql> create procedure p_inout(inout p1 int)
-> begin
-> if (p1 is not null) then
-> set p1=p1+1;
-> end if;
-> end$$
Query OK, 0 rows affected (0.01 sec)
mysql> \d ;
mysql> select @n
-> ;
+------+
| @n |
+------+
| NULL |
+------+
mysql> call p_inout(@n);
Query OK, 1 row affected (0.00 sec)
mysql> select @n;
+------+
| @n |
+------+
| 100 |
+------+
1 row in set (0.00 sec)
mysql> call p_inout(@n);
Query OK, 0 rows affected (0.00 sec)
mysql> select @n;
+------+
| @n |
+------+
| 101 |
+------+
1 row in set (0.00 sec)
存储函数的创建
MySQL存储函数(自定义函数),函数一般用于计算和返回一个值,可以将经常需要使用的计算或功能写成一个函数。
在MySQL中,存储函数的使用方法与MySQL内部函数的使用方法基本相同。用户自定义的存储函数与MySQL内部函数性质相同。区别在于,存储函数是用户自定义的。而内部函数由MySQL自带。
格式
创建
CREATE FUNCTION func_name ([param_name type[,...]])
RETURNS type
[characteristic ...]
BEGIN
routine_body
END;
(1)func_name :存储函数的名称。
(2)param_name type:可选项,指定存储函数的参数。
type参数用于指定存储函数的参数类型,该类型 可以是MySQL数据库中所有支持的类型。
(3)RETURNS type:指定返回值的类型。
(4)characteristic:可选项,指定存储函数的特性。
(5)routine_body:SQL代码内容。
调用
SELECT func_name([parameter[,…]]);
修改
ALTER FUNCTION func_name [characteristic ...]
characteristic:
COMMENT 'string'
| LANGUAGE SQL
| {
CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY {
DEFINER | INVOKER }
查看
SHOW STATUS 查看存储过程和函数的状态
SHOW FUNCTION STATUS [LIKE 'pattern']
删除
DROP FUNCTION IF EXISTS 函数名;
1418错误
mysql> \d $$
mysql> create function f1()
-> returns int
-> begin
-> declare n int default 0;
-> select cunt(1) into n from emp;
-> return n;
-> end $$
ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
错误原因
MySQL开启bin-log后,调用存储过程或者函数以及触发器时,会出现错误号为1418的错误
解决办法
信任子程序的创建者,禁止创建、修改子程序时对SUPER权限的要求,设置 log_bin_trust_routine_creators全局系统变量为1。
(1)在客户端上执行 SET GLOBAL log_bin_trust_function_creators = 1;
(2)MySQL启动时,加上--log-bin-trust-function-creators选贤,参数设置为1 。
(3)在MySQL配置文件my.ini或my.cnf中的[mysqld]段上加log-bin-trust-function- creators=1。
实例
例一
1、修改1418错误
mysql> SET GLOBAL log_bin_trust_function_creators = 1$$
2、创建存储函数
mysql> \d $$
mysql> create function f1()
-> returns int
-> begin
-> declare n int default 0;
-> select count(1) into n from emp;
-> return n;
-> end $$
mysql> \d ;
3、调用函数
mysql> select f1();
+------+
| f1() |
+------+
| 15 |
+------+
例二
mysql> select * from emp;
+-------+-----------+-----------+------+------------+-------+-------+--------+
| empno | ename | job | mgr | hiredate | sai | comm | deptno |
+-------+-----------+-----------+------+------------+-------+-------+--------+
| 1001 | 甘宁 | 文员 | 1013 | 2000-12-17 | 8000 | NULL | 20 |
| 1002 | 黛绮丝 | 销售员 | 1006 | 2001-02-20 | 16000 | 3000 | 30 |
| 1003 | 殷天正 | 销售员 | 1006 | 2001-02-22 | 12500 | 5000 | 30 |
| 1004 | 刘备 | 经理 | 1009 | 2001-04-02 | 29750 | NULL | 20 |
| 1005 | 谢逊 | 销售员 | 1006 | 2001-09-28 | 12500 | 14000 | 30 |
| 1006 | 关羽 | 经理 | 1009 | 2001-05-01 | 28500 | NULL | 30 |
| 1007 | 张飞 | 经理 | 1009 | 2001-09-01 | 24500 | NULL | 10 |
| 1008 | 诸葛亮 | 分析师 | 1004 | 2007-04-19 | 30000 | NULL | 20 |
| 1009 | 曾阿牛 | 董事长 | NULL | 2001-11-17 | 50000 | NULL | 10 |
| 1010 | 韦一笑 | 销售员 | 1006 | 2001-09-08 | 15000 | 0 | 30 |
| 1011 | 周泰 | 文员 | 1006 | 2007-05-23 | 11000 | NULL | 20 |
| 1012 | 程普 | 文员 | 1006 | 2001-12-03 | 9500 | NULL | 30 |
| 1013 | 庞统 | 分析师 | 1004 | 2001-12-03 | 30000 | NULL | 20 |
| 1014 | 黄盖 | 文员 | 1007 | 2002-01-23 | 13000 | NULL | 10 |
| 1015 | 张三 | 保洁员 | 1001 | 2013-05-01 | 80000 | 50000 | 50 |
+-------+-----------+-----------+------+------------+-------+-------+--------+
1、创建函数
mysql> \d $$
mysql> create function f2(name varchar(20))
-> returns int
-> begin
-> declare salary int;
-> select sai into salary from emp
-> where ename=name;
-> return salary;
-> end$$
mysql> \d ;
2、调用函数
mysql> select f2('黄盖')$$
+--------------+
| f2('黄盖') |
+--------------+
| 13000 |
+--------------+
mysql> select f2('张飞') 张飞工资;
+--------------+
| 张飞工资 |
+--------------+
| 24500 |
+--------------+
删除函数
mysql> drop function f2;
Query OK, 0 rows affected (0.00 sec)
作业实例
mysql> create table sch (
-> id int primary key ,
-> name varchar(50),
-> glass varchar(50)
-> );
mysql> insert into sch values(1,'xiaoming','glass 1');
mysql> insert into sch values(2,'xiaohong','glass 2');
1、创建一个可以统计表格内记录条数的存储函数 ,函数名为count_sch()
mysql> SET GLOBAL log_bin_trust_function_creators = 1$$
mysql> \d $$
mysql> create function count_sch()
-> returns int
-> begin
-> declare n int default 0;
-> select count(1) into n from sch;
-> return n;
-> end $$
mysql> \d ;
mysql> select count_sch();
+-------------+
| count_sch() |
+-------------+
| 2 |
+-------------+
2、创建一个存储过程avg_sai,有3个参数,分别是deptno,job,接收平均工资,功能查询emp表dept为30,job为销售员的平均工资。(使用之前的emp表)
mysql> \d $$
mysql> create procedure avg_sai(in p1 int,in p2 varchar(20),out p3 float)
-> begin
-> select avg(sai) into p3 from emp where deptno=p1 and job=p2;
-> end $$
Query OK, 0 rows affected (0.10 sec)
mysql> \d ;
mysql> call avg_sai(30,'销售员',@a_sai);
Query OK, 1 row affected (0.00 sec)
mysql> select @a_sai;
+--------+
| @a_sai |
+--------+
| 14000 |
+--------+
1 row in set (0.00 sec)