MySQL存储过程与存储函数
1、创建存储过程
存储过程就是一条或者多条 SQL 语句的集合,可以视为批文件。它可以定义批量插入的语句,也可以定义一个接收不同条件的 SQL。
创建存储过程的语句为 “create procedure”,创建存储函数的语句为 “create function”。
调用存储过程的语句为 “CALL”。
调用存储函数的形式就像调用 MySQL 内部函数一样。
DROP TABLE IF EXISTS t_student;CREATE TABLE t_student
(id INT(11) PRIMARY KEY AUTO_INCREMENT,name VARCHAR(255) NOT NULL,age INT(11) NOT NULL
);INSERT INTO t_student VALUES(NULL,'大宇',22),(NULL,'小宇',20);
如上述,t_student 表中的数据有两条。如果我们要分别查询出来这两条数据,显然就是根据 ID 来查询。查询出来了第一条数据后,我们可能会去做其它的操作。等过两天,我们要查询另外一条记录的时候,可能又要再写一次这样的查询语句。
存储过程和存储函数应运而生,这样就可以对某些 SQL 语句进行封装,从而实现功能的复用。
定义一个根据 ID 查询学生记录的存储过程:
DROP PROCEDURE IF EXISTS getStuById;DELIMITER // -- 定义存储过程结束符号为//
CREATE PROCEDURE getStuById(IN stuId INT(11),OUT stuName VARCHAR(255),OUT stuAge INT(11)) -- 定义输入与输出参数
COMMENT 'query students by their id' -- 提示信息
SQL SECURITY DEFINER -- DEFINER指明只有定义此SQL的人才能执行,MySQL默认也是这个
BEGINSELECT name ,age INTO stuName , stuAge FROM t_student WHERE id = stuId; -- 分号要加
END // -- 结束符要加
DELIMITER ; -- 重新定义存储过程结束符为分号
语法:“create procedure sp_name(定义输入输出参数) 【存储特性】begin SQL语句;END”
IN 表示输入参数;OUT 表示输出参数;INOUT 表示既可以输入也可以输出的参数;sp_name 为存储过程的名字。
如果此存储过程没有任何输入、输出,其实就没有什么意义了,但是 sp_name() 的括号不能省略。
查看刚才创建的存储过程。
SHOW PROCEDURE STATUS LIKE 'g%'
下面是调用储存过程。对于存储过程提供的临时变量而言,MySQL 规定要加上 “@” 开头。
#study 是当前数据库名称CALL study.getStuById(1,@name,@age);SELECT @name AS stuName,@age AS stuAge;
CALL getStuById(2,@name,@age);SELECT @name AS stuName,@age AS stuAge;
这样的好处是,如果一段较为复杂的 SQL 语句,我们可能过了几天再去写它,又费时费力。存储过程可以
封装我们写过的 SQL,在下次只需要调用它的时候,直接提供参数并指明查询结果输出到哪些变量中即可。
提示:如果存储过程一次查询出两个记录,将会提示出错。"[Err] 1172 - Result consisted of more than one row"。
所以需要在存储过程的 SQL 后面加上 “limit 1”。从位偏移量为 0 的,即从查询结果的第一条数据开始,查询一条记录。
2、创建存储函数
存储函数与存储过程本质上是一样的,都是封装一系列 SQL 语句,简化调用。
我们自己编写的存储函数可以像 MySQL 函数那样自由的被调用。
DROP FUNCTION IF EXISTS getStuNameById;DELIMITER //
CREATE FUNCTION getStuNameById(stuId INT) -- 默认是IN,但是不能写上去。stuId视为输入的临时变量
RETURNS VARCHAR(255) -- 指明返回值类型
RETURN (SELECT name FROM t_student WHERE id = stuId); // -- 指明SQL语句,并使用结束标记。注意分号位置
DELIMITER ;
使用存储函数:
SELECT getStuNameById(1);
提示:在 return 语句后面,有趣的是,分号在 SQL 语句的外面。如果不加分号,查询结果居然查询出两条记录。
从上述存储函数的写法上来看,存储函数有一定的缺点。首先与存储过程一样,只能返回一条结果记录。另外就是存储函数只能指明一列数据作为结果,而存储过程能够指明多列数据作为结果。
3、定义变量
如果希望 MySQL 执行批量插入的操作,那么至少要有一个计数器来计算当前插入的是第几次。
这里的变量是用在存储过程中的 SQL 语句中的,变量的作用范围在 “begin … end” 中。
没有 default 子句,初始值为 NULL。
定义变量的操作:
DECLARE name,address VARCHAR; -- 发现了吗,SQL中一般都喜欢先定义变量再定义类型,与Java是相反的。
DECLARE age INT DEFAULT 20; -- 指定默认值。若没有DEFAULT子句,初始值为NULL。
为变量赋值:
SET name = 'jay'; -- 为name变量设置值DECLARE var1,var2,var3 INT;
SET var1 = 10,var2 = 20; -- 其实为了简化记忆其语法,可以分开来写
-- SET var1 = 10;
-- SET var2 = 20;
SET var3 = var1 + var2;
使用变量实例。如下表,在做了去除主键约束后,我又添加了一条 “id=1” 的数据。现在希望查询出 “id=1” 记录的数量。
DROP PROCEDURE IF EXISTS contStById;DELIMITER // -- 定义存储过程结束符号为//
CREATE PROCEDURE contStById(IN sid INT(11),OUT result INT(11)) -- 定义输入变量
BEGINDECLARE sCount INT;SELECT COUNT(*) INTO sCount FROM t_student WHERE id = sid;SET result = sCount; -- 用变量为输出结果设值
END // -- 结束符要加
DELIMITER ; -- 重新定义存储过程结束符为分号CALL contStById(1,@result);
SELECT @result;
显然,在存储过程中的变量,可以直接与输出变量进行相应的计算。本例直接把 “sCount” 这个变量的值赋值到输出中。
4、定义条件与定义处理程序
定义条件 condition:指的是在执行存储过程中的 SQL 语句时,可能出现的问题;
定义处理程序 handler:当遇到了指定问题时应该如何处理,避免存储过程因执行异常而停止。
定义条件和定义处理程序的位置应该在 “begin … end” 之间。
定义条件的语法:“declare condition_name condition for 错误码或错误值;”
错误码可以视为一个错误的引用,比如 404,它代表的就是找不到页面的错误,而它的错误值可能为 Null Pointer Exception。
DECLARE command_not_allowed CONDITION FOR SQLSTATE '42000'; -- 错误值
DECLARE command_not_allowed CONDITION FOR 1148; -- 错误码
定义处理程序语法:“declare handler_type handler for condition_name sp_statement;”
handler_type 的值有三种,其中 MySQL支持的有两种: continue 是指遇到错误忽略,继续执行下面的 SQL。exit 表示遇到错误退出,默认的策略就是 exit。(undo 表示遇到错误后撤回之前的操作,MySQL 目前还不支持)
condition_name 可以是我们自己定义的条件,也可以是 MySQL 内置的条件,比如 SQL WARNING。sp_statement 指遇到错误的时候,需要执行饿存储过程或存储函数。
DECLARE CONTINUE HANDLER FOR SQLSATTE '42S02' SET @info = 'NO_SUCH_TABLE'; -- 忽略错误值为42S02的SQL异常DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info = 'ERROR_OCCUR'; -- 捕获SQL执行异常并输出信息DECLARE no_such_table CONDITION FOR 1146; -- 为错误码为1146的错误定义条件
DECLARE CONTINUE HANDLER FOR no_such_table SET @info = 'no_such_table'; -- 为指定的条件设置处理程序
DROP TABLE IF EXISTS t_student;CREATE TABLE t_student
(id INT(11) PRIMARY KEY AUTO_INCREMENT,name VARCHAR(255) NOT NULL,age INT(11) NOT NULL
);
现在通过存储过程,为这张表插入数据。因为 id 属性有主键约束,所以不能插入相同的 id。
DROP PROCEDURE IF EXISTS insertStu;DELIMITER // -- 定义存储过程结束符号为//
CREATE PROCEDURE insertStu(OUT result INT) -- 指定输出结果
BEGINDECLARE flag INT(11) DEFAULT 0; -- 指定变量为0DECLARE primary_key_limit CONDITION FOR SQLSTATE '23000'; -- 主键约束的错误值DECLARE CONTINUE HANDLER FOR primary_key_limit SET @info = -1; -- 设计如果出现错误,@info将会被设置为 -1 INSERT INTO t_student(id,name,age) VALUES(1,'dayu',22); -- 插入值,设置主键为1SET flag = 1; -- 普通变量设值为1SET result = flag; -- 如果下面的SQL执行出现异常,那么就退出,只有上面的SQL生效。将普通变量的值给输出INSERT INTO t_student(id,name,age) VALUES(1,'dayu',22); -- 插入值,设置主键为1SET flag = 2; -- 如果处理程序是EXIT,那么就不会执行到这一步了SET result = flag; -- 将普通变量的值给输出
END // -- 结束符要加
DELIMITER ; -- 重新定义存储过程结束符为分号
continue 是指遇到错误忽略,继续执行下面的 SQL。因为是 continue 来处理程序,所以遇到错误后将会继续执行。
另外,第二次插入记录,因为违反了主键约束,所以插入失败,但是存储过程仍然继续执行完毕。
CALL insertStu(@result);
SELECT @result,@info; -- @info没有申明就能调用到,可能是是全局变量吧
运行结果:
再次查看 t_student 表,只插入了一条记录,但是所有的存储过程都执行完毕了。
现在,重新执行下面的 SQL。先重新建表,再将处理程序的处理策略换位 exit;在执行存储过程中遇到了错误,那么就立即退出。
DROP TABLE IF EXISTS t_student;CREATE TABLE t_student
(id INT(11) PRIMARY KEY AUTO_INCREMENT,name VARCHAR(255) NOT NULL,age INT(11) NOT NULL
);DROP PROCEDURE IF EXISTS insertStu;DELIMITER // -- 定义存储过程结束符号为//
CREATE PROCEDURE insertStu(OUT result INT) -- 指定输出结果
BEGINDECLARE flag INT(11) DEFAULT 0; -- 指定变量为0DECLARE primary_key_limit CONDITION FOR SQLSTATE '23000'; -- 主键约束的错误值DECLARE EXIT HANDLER FOR primary_key_limit SET @info = -1; -- 使用EXIT策略,遇到SQL错误将会结束这次存储过程-- 出现SQL错误则直接退出存储过程的执行INSERT INTO t_student(id,name,age) VALUES(1,'dayu',22); -- 插入值,设置主键为1SET flag = 1; -- 普通变量设值为1SET result = flag; -- 如果下面的SQL执行出现异常,那么就退出,只有上面的SQL生效。将普通变量的值给输出INSERT INTO t_student(id,name,age) VALUES(1,'dayu',22); -- 插入值,设置主键为1SET flag = 2; -- 如果处理程序是EXIT,那么就不会执行到这一步了SET result = flag; -- 将普通变量的值给输出
END // -- 结束符要加
DELIMITER ; -- 重新定义存储过程结束符为分号CALL insertStu(@result);
SELECT @result,@info; -- @info没有申明就能调用到,可能是是全局变量吧
@result 的结果为 1,说明执行第二条 SQL 的时候,出现了异常。同样,@info 的值为 -1,也提示处理条件中定义的存储过程被触发。
最后,数据库表中的数据也是:
如果都是正确的 SQL,会是什么情况呢?
DROP TABLE IF EXISTS t_student;CREATE TABLE t_student
(id INT(11) PRIMARY KEY AUTO_INCREMENT,name VARCHAR(255) NOT NULL,age INT(11) NOT NULL
);DROP PROCEDURE IF EXISTS insertStu;DELIMITER // -- 定义存储过程结束符号为//
CREATE PROCEDURE insertStu(OUT result INT) -- 指定输出结果
BEGINDECLARE flag INT(11) DEFAULT 0; -- 指定变量为0DECLARE primary_key_limit CONDITION FOR SQLSTATE '23000'; -- 主键约束的错误值DECLARE EXIT HANDLER FOR primary_key_limit SET @info = -1; -- 设计如果出现错误,@info将会被设置为 -1 INSERT INTO t_student(id,name,age) VALUES(NULL,'dayu',22); -- SET flag = 1; -- 普通变量设值为1SET result = flag; -- 如果下面的SQL执行出现异常,那么就退出,只有上面的SQL生效。将普通变量的值给输出INSERT INTO t_student(id,name,age) VALUES(NULL,'dayu',22); -- SET flag = 2; -- 如果处理程序是EXIT,那么就不会执行到这一步了SET result = flag; -- 将普通变量的值给输出
END // -- 结束符要加
DELIMITER ; -- 重新定义存储过程结束符为分号CALL insertStu(@result);
SELECT @result,@info; -- @info没有申明就能调用到,可能是是全局变量吧
6、流程控制的使用
(1)if 语句的使用
DROP PROCEDURE IF EXISTS testIf;
DELIMITER //
CREATE PROCEDURE testIf(OUT result VARCHAR(255))
BEGINDECLARE val VARCHAR(255);SET val = 'a';IF val IS NULLTHEN SET result = 'IS NULL';ELSE SET result = 'IS NOT NULL';END IF;
END //
DELIMITER ;CALL testIf(@result);
SELECT @result;
(2)case 语句
DROP PROCEDURE IF EXISTS testCase;
DELIMITER //
CREATE PROCEDURE testCase(OUT result VARCHAR(255))
BEGINDECLARE val VARCHAR(255);SET val = 'a';CASE val IS NULLWHEN 1 THEN SET result = 'val is true';WHEN 0 THEN SET result = 'val is false';ELSE SELECT 'else';END CASE;
END //
DELIMITER ;CALL testCase(@result);
SELECT @result;
(3)loop
loop 用于重复执行 SQL。leave 用于退出循环。
DROP PROCEDURE IF EXISTS testLoop;
DELIMITER //
CREATE PROCEDURE testLoop(OUT result VARCHAR(255))
BEGINDECLARE id INT DEFAULT 0;add_loop:LOOPSET id = id + 1;IF id>10 THEN LEAVE add_loop; -- 可在此处修改成批量插入END IF;SET result = id;END LOOP add_loop;
END //
DELIMITER ;CALL testLoop(@result);
SELECT @result;
下面是一个批量插入的例子:
DROP TABLE IF EXISTS t_student;CREATE TABLE t_student
(id INT(11) PRIMARY KEY AUTO_INCREMENT,name VARCHAR(255) NOT NULL,age INT(11) NOT NULL
);DROP PROCEDURE IF EXISTS testLoop;
DELIMITER //
CREATE PROCEDURE testLoop(IN columnCount INT(11))
BEGINDECLARE id INT DEFAULT 0;add_loop:LOOPSET id = id + 1;IF id>columnCount THEN LEAVE add_loop;END IF;INSERT INTO t_student(id,name,age) VALUES(id,'dayu',22);END LOOP add_loop;
END //
DELIMITER ;CALL testLoop(15);
(4)while
DROP PROCEDURE IF EXISTS testWhile;
DELIMITER //CREATE PROCEDURE testWhile(IN myCount INT(11),OUT result INT(11))
BEGINDECLARE i INT DEFAULT 0 ; -- 定义变量WHILE i < myCount DO -- 符合条件就循环-- 核心循环SQL; SET i = i + 1 ; -- 计数器+1END WHILE; -- 当不满足条件,结束循环 --分号一定要加!SET result = i; -- 将变量赋值到输出
END //
CALL testWhile(10,@result);
SELECT @result AS 循环次数;
7、使用 “show status” 查看存储过程或函数的状态
SHOW PROCEDURE STATUS LIKE 'C%';
SHOW FUNCTION STATUS LIKE 'C%';
知道了存储过程,如果希望查看具体的存储过程或者存储函数的定义:
SHOW CREATE PROCEDURE study.CountStu;-- Create Procedure 列为核心语句
CREATE DEFINER=`root`@`localhost` PROCEDURE `CountStu`(IN stu_sex CHAR,OUT num INT)
BEGIN SELECT COUNT(*) INTO num FROM t_student WHERE sex = stu_sex;
END
提示:带上数据库的名字,小心查询不到。
查看存储函数有哪些:
SHOW FUNCTION STATUS LIKE 'C%'
查看具体的存储函数创建语句:
SHOW CREATE FUNCTION study.countStu2-- Create Function 列的语句
CREATE DEFINER=`root`@`localhost` FUNCTION `countStu2`(stu_sex CHAR)
RETURNS int(11)
RETURN (SELECT COUNT(*) FROM t_student WHERE sex = stu_sex)
8、从 information_schema.Routines 表中查询存储过程与函数
原来,MySQL 中的存储过程与存储函数都存放在 information_schema 数据库下的 Routines 表中。
SELECT * FROM information_schema.ROUTINES WHERE ROUTINE_NAME LIKE 'C%'
如果什么时候忘记了存储函数或者存储过程的名字,可以查询这张表的数据。然后确定了是某个存储过程或者是存储函数,就可以使用 “show create procedure/function 数据库.sp_name” 查看指定的创建语句了。
9、修改存储过程
语法:“alter procedure | function sp_name [存储特性]”
修改存储过程,将读写权限改为 MODIFIES SQL DATE 并指明调用者
ALTER PROCEDURE countStu2
MODIFIES SQL DATE -- 表示子程序中包含写数据的语句
SQL SECURITY INVOKER -- 表示调用者才能执行
存储过程与存储函数的补充
1、存储过程如何修改代码?
虽然提供了 “alter procedure sp_name [存储特性]”,但是只能修改存储过程的存储特性,不能修改 SQL。需要删除并重新创建。
2、存储过程中能调用其它存储过程吗?
可以在存储过程中的 SQL 中通过 CALL 调用其它存储过程,但是不能用 drop 删除其它存储过程。
3、存储过程中的 in 参数可能是中文怎么办?
在定义存储过程的时候,加上 “character set gbk”
DELIMITER //
CREATE PROCEDURE getAddressByName(IN u_name VARCHAR(50) character set gbk , OUT address VARCHAR(50))
BEGINSQL;
END//
DELIMITER ;
MySQL存储过程与存储函数相关推荐
- Mysql存储过程和存储函数
存储过程和存储函数 前言 存储过程的相关操作 创建 调用 查看 删除 语法 变量的定义与赋值 if条件判断 参数 case结构 while循环 repeat循环结构 loop语句 leave语句 游标 ...
- mysql 存储过程与存储函数
第一节:存储过程和函数的引入 存储过程和函数是在数据库中定义一些SQL 语句的集合,然后直接调用这些存储过程和函数来执行已经定义好 的SQL 语句.存储过程和函数可以避免开发人员重复的编写相同的SQL ...
- MySQL 6:MySQL存储过程、存储函数
MySQL 5.0 版本开始支持存储过程.存储过程是一组SQL语句,功能强大,可以实现一些复杂的逻辑功能,类似于JAVA语言中的方法:存储是数据库SQL语言层面的代码封装和复用. 存储过程有输入输出参 ...
- 【Mysql 存储过程 Or 存储函数 傻傻分不清? 】
MySQL的存储函数(自定义函数)和存储过程都是用于存储SQL语句的.但是什么时候用什么呢?是不是总是傻傻的分不清? 本文来详细的讲一下存储函数 和存储过程 ,以后再也不会迷糊. 存储函数 | 存储过 ...
- MySQL初级篇——存储过程、存储函数的相关概念及应用举例
文章目录: 1.什么是存储过程? 2.存储过程操作相关SQL 3.存储过程实操SQL 4.存储函数操作相关SQL 5.存储函数实操SQL 6.存储过程.存储函数的优缺点 1.什么是存储过程? 含义:存 ...
- MYSQL中如何创建存储过程和存储函数(上篇)
存储程序分为存储过程和存储函数.在MySQL中创建存储过程和存储函数的语句分别是create procedure 和create function.使用call语句来调用存储过程,只能用输出变量返回值 ...
- 【宋红康 MySQL数据库】【基础版】【15】存储过程与存储函数
文章目录 存储过程与存储函数 定义存储过程与存储函数 对比存储函数和存储过程 存储过程概述 理解 分类 创建存储过程 语法分析 代码举例 调用存储过程 调用格式 代码举例 如何调试 存储函数的使用 语 ...
- 【MySQL】存储过程与存储函数
存储过程与存储函数 1 存储过程 1.1介绍 存储过程是事先经过编译并存储在数据库中的一段SQL 语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提 ...
- MySQL(六)——存储过程和存储函数
前言 今天简单的介绍一下"存储函数"和"存储过程",平时在工作中用到的时间不多,时间长了难免会忘记.在这里简单的做个回忆总结,方便自己以后复习回忆,当然能帮到需 ...
最新文章
- NTT DOCOMO将部署多供应商NFV技术
- pl/sql中建用户
- golang hmac的sha1加密例子
- handler和thread之间如何传输数据_网线虽常见,学问可不少,科普一下网线的简单知识及如何选择网线...
- 【简洁易懂】Filter的四种拦截方式
- SOCK_DGRAM(数据报套接字)与SOCK_STREAM(流套接口)的区别
- 论文浅尝 | 基于知识图的问答变分推理
- 惯导标定国内外研究现状小结(删减版)
- 把一个人的特点写具体作文_把一个人的特点写具体作文600字
- kali升级操作系统
- 计算机系统——汇编语言基础
- 综合案例:使用Scanner,Random,ArrayList完成一个不重复的点名程序
- CPU锁频率在0.78 GHz
- Halo博客网站添加天气插件
- wait与sleep的讲解(wait有参及无参区别)
- arch linux格式化,用Arch linux打造自己的操作系统(一)
- java用list集合实现对数据的增加,删除,插入操作
- 智慧政务大数据 政务综合服务平台建设项目方案书(word)
- vue引用echarts折线平滑面积图
- 用C语言编写的骂人的软件,自动骂人软件充斥网络 上网聊天竟被骂