数据库-存储过程与函数

概念

存储过程是一组为了完成特定功能的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)

猜你喜欢

转载自blog.csdn.net/m0_46289868/article/details/112987770