存储过程
1.存储过程是什么
存储过程是⼀组为了完成特定功能的SQL语句集,经编译后存储在数据库中,⽤⼾通过指定存储
过程的名字和参数来执⾏,并获取相应的结果。

2.优点&缺点
性能优化:存储过程在创建时编译并存储在数据库中,执⾏速度⽐单个SQL语句快。
安全性:可以限制⽤⼾直接访问数据库,通过存储过程间接访问,从⽽保证系统安全性。**降低耦合:**当表结构发⽣变化时,只需要修改相应的存储过程,应⽤程序的改动较⼩。
不适合⾼并发场景:在⾼并发场景下,存储过程可能会增加数据库的压⼒,难以维护。
可移植性差 :存储过程不能跨数据库移植,更换数据库时需要重新编写。
3.存储过程相关语法
创建
sql
-- 修改SQL语句结束标识符为 //
DELIMITER //
-- 创建存储过程
CREATE PROCEDURE 存储过程名 (参数列表)
BEGIN
-- SQL 语句
END //
-- 修改SQL语句结束标识符为 ;
DELIMITER ;
调用
sql
-- 调⽤存储过程
CALL 存储过程名 (参数列表);
查看
sql
-- 查看指定数据库中创建的存储过程
SELECT * FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = '数据库名';
-- 查看存储过程的定义
SHOW CREATE PROCEDURE 存储过程名;
删除
sql
DROP PROCEDURE [IF EXISTS] 存储过程名;
示例
sql
CREATE PROCEDURE p_calAvg()
BEGIN
select name, chinese + math + english as total from exam;
END;
-- 调⽤存储过程
CALL p_calAvg();
可以发现,这里没有使用DELIMITER进行切换sql结束符,因为这个是在可视化工具里面使用的,会自动进行处理,但是如果是需要往云服务器上传代码的话,需要手动去修改,因为云服务器中使用的是mysql客户端

变量
在MySQL中变量可以分为三类: 系统变量、⽤⼾⾃定义变量、以及局部变量
1.系统变量
系统变量是MySQL服务器的配置变量,控制着服务器的⾏为和性能。分为全局变量(GLOBAL)和会话变量(SESSION)
查看系统变量
sql
-- 查看所有系统变量
SHOW [GLOBAL|SESSION] VARIABLES;
-- 查看指定的系统变量
SHOW [GLOBAL|SESSION] VARIABLES LIKE 'xxx';
-- 查看指定的系统变量,可以通过LKIE进⾏模糊查询
SHOW [GLOBAL|SESSION] VARIABLES like '%xxx%';
-- 使⽤SELECT查看指定系统变量
SELECT @@[GLOBAL|SESSION].系统变量名;
-- ----------------------------------------------------
-- ⽰例:查看以auto开头的全局系统变量
SHOW GLOBAL VARIABLES LIKE 'auto%';
-- ⽰例:查看以char开头的会话系统变量
SHOW SESSION VARIABLES LIKE 'char%';
-- ⽰例:查看事务⾃动提交全局系统变量
SELECT @@GLOBAL.autocommit;
设置系统变量
sql
SET [GLOBAL|SESSION] 系统变量名 = 值; 如果不指定,默认是session
SET @@SESSION.系统变量名 = 值;
-- -------------------------------------------
-- ⽰例:设置事务⾃动提交会话变量为关闭/开启
SET @@SESSION.autocommit = 0;
SET autocommit = 1;
- 如果没有指定GLOBAL|SESSION,默认设置会话(SESSION)全局变量
- 会话关闭后,设置的会话(SESSION)变量失效;新建的会话读取全局系统变量的值做为初始值。
- MySQL重启后,设置的全局(GLOBAL)变量失效,如果想使全局系统变量永久⽣效,需要修改选项⽂件
2.⽤⼾⾃定义变量
⽤⼾⾃定义变量是在SQL会话中定义的变量,不⽤提前声明,作⽤域为当前会话。
⽅式 【推荐】
SET @var_name := expr [, @var_name]...;
-- ⽰例:定义⼀个age变量并赋值为18,并查看
SET @age := 18;
SELECT @age;
由于SQL中⽐较⽐较相等也是⽤等号 (=),所以在为变量赋值的时候推荐使⽤ ( := )
3.局部变量
局部变量只在存储过程、函数或触发器的范围内有效。需要使⽤ DECLARE 声明,作⽤域的范围在声明的 BEGIN ... END 块内。
声明
DECLARE 变量名 变量类型 [DEFAULT 默认值] ...;
赋值
SET var_name := 值;
使⽤
-- ⽰例:在存储过程中定义局部变量记录学⽣表的总记录数
CREATE PROCEDURE p1()
BEGIN
-- 定义局部变量,并指定默认值
DECLARE stu_count INT DEFAULT 0;
-- 把查询结果赋值给局部变量
select count(*) into stu_count from student;
-- 使⽤局部变量
select stu_count;
END;
-- 调⽤存储过程
call p1();
• 变量名不区分⼤⼩写。
• 在存储过程和函数中,局部变量必须在使⽤前声明。
• ⽤⼾⾃定义变量在会话结束时失效,⽽局部变量在存储过程或函数结束时失效。
• 避免使⽤保留字作为变量名
SQL编程
1.条件判断 - IF 语句
sql
IF 条件1 THEN
......
[ELSEIF 条件2 THEN
......
ELSE
......]
END IF;
示例

sql
-- 创建存储过程
CREATE PROCEDURE p2()
BEGIN
-- 定义初始分数变量
DECLARE score INT DEFAULT 86;
-- 定义等级结果变量
DECLARE result VARCHAR(10);
-- 判断
IF score >= 90 THEN
SET result := '优秀';
ELSEIF score >= 80 AND score < 90 THEN
SET result := '良好';
ELSEIF score >= 60 AND score < 80 THEN
SET result := '及格';
ELSE
SET result := '不及格';
END IF;
-- 查询结果
SELECT result;
END;
-- 调⽤存储过程
CALL p2();
2.参数
sql
IN 输⼊类型,调⽤存储过程时要传⼊的值,默认参数类型
OUT 输出类型,可以作为存储过程的返回值
INOUT 输⼊输出类型,即可以作为输⼊类型也可以做为输出类型
语法
sql
-- 修改SQL语句结束标识符为 //
DELIMITER //
-- 创建存储过程
CREATE PROCEDURE 存储过程名 ([IN/OUT/INOUT 参数名 参数类型] [,...])
BEGIN
-- SQL 语句
END //
-- 修改SQL语句结束标识符为 ;
DELIMITER ;
示例

sql
-- 创建存储过程
CREATE PROCEDURE p3(IN score INT, OUT result VARCHAR(10))
BEGIN
-- 判断
IF score >= 90 THEN
SET result := '优秀';
ELSEIF score >= 80 AND score < 90 THEN
SET result := '良好';
ELSEIF score >= 60 AND score < 80 THEN
SET result := '及格';
ELSE
SET result := '不及格';
END IF;
END;
3.CASE
语法
sql
-- 语法⼀
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list] ...
[ELSE statement_list]
END CASE
sql
-- 语法⼆
CASE
WHEN search_condition THEN statement_list
[WHEN search_condition THEN statement_list] ...
[ELSE statement_list]
END CASE
示例

sql
-- 创建存储过程
CREATE PROCEDURE p5(IN code INT, OUT result varchar(50))
BEGIN
CASE code
WHEN 0 THEN
SET result := '成功';
WHEN 10001 THEN
SET result := '⽤⼾名或密码错误';
WHEN 10002 THEN
SET result := '您没有对应的权限';
WHEN 20001 THEN
SET result := '你传⼊的参数有误';
WHEN 20002 THEN
SET result := '没有找到相应的结果';
ELSE
SET result := '服务器错误,请联系管理员';
END CASE;
END;
-- 调⽤存储过程
CALL p5(10001, @result);
-- 查看结果
SELECT @result;

sql
-- 创建存储过程
CREATE PROCEDURE p6(IN month INT, OUT result varchar(50))
BEGIN
CASE
WHEN month >= 1 AND month <= 3 THEN
SET result := '第⼀季度';
WHEN month >= 4 AND month <= 6 THEN
SET result := '第⼆季度';
WHEN month >= 7 AND month <= 9 THEN
SET result := '第三季度';
WHEN month >= 10 AND month <= 12 THEN
SET result := '第四季度';
ELSE
SET result := '⾮法参数';
END CASE;
END;
-- 调⽤存储过程
CALL p6(6, @result);
-- 查询结果
SELECT @result;
4.循环
WHERE
先判断条件表达式 search_condition 是否为 TRUE ,如果条件成⽴,则执⾏循环体中的
statement_list
sql
WHILE search_condition DO
statement_list
END WHILE;
⽰例:传⼊⼀个数n,计算从1累加到n的值
sql
-- 创建存储过程
CREATE PROCEDURE p7(IN n INT)
BEGIN
-- 定义⼀个变量,表⽰结果
DECLARE total int DEFAULT 0;
-- 累加
WHILE n > 0 DO
SET total := total + n;
SET n := n - 1;
END WHILE;
-- 查询结果
SELECT total;
END;
-- 调⽤存储过程
CALL p7(100);
REPEAT
语法
sql
REPEAT
statement_list
UNTIL search_condition
END REPEAT;
⽰例:传⼊⼀个数n,计算从1累加到n的值
sql
-- 创建存储过程
CREATE PROCEDURE p8(IN n INT)
BEGIN
-- 定义⼀个变量,表⽰结果
DECLARE total int DEFAULT 0;
-- 累加
REPEAT
SET total := total + n;
SET n := n - 1;
UNTIL n <= 0 END REPEAT;
-- 查询结果
SELECT total;
END;
-- 调⽤存储过程
CALL p8(100);
LOOP
LOOP也可以实现⼀个简单的循环,并且当满⾜某个条件时终⽌当前循环或退出整个循环,通常配
合以下两个⼦句使⽤
- LEAVE label:退出整个循环,类似于C++或JAVA中的 break;
- ITERATE label: 终⽌当前循环,进⼊下⼀次循环,类似于C++或JAVA中的 continue;
sql
[begin_label:] LOOP
statement_list
END LOOP [end_label]

sql
-- 创建存储过程
CREATE PROCEDURE p9(IN n INT)
BEGIN
-- 定义⼀个变量,表⽰结果
DECLARE total int DEFAULT 0;
-- 累加
sum_label: LOOP
-- 判断是否退出
IF n <= 0 THEN
LEAVE sum_label;
END IF;
-- 累加操作
SET total := total + n;
SET n := n - 1;
END LOOP sum_label;
-- 查询结果
SELECT total;
END;
-- 调⽤存储过程
CALL p9(100);
游标
MySQL中的游标是⼀种数据库对象,允许在存储过程和函数中对查询到的结果集进⾏逐⾏检索。
使⽤游标之前必须先声明游标,之后使⽤ OPEN 、 FETCH 和 CLOSE 语句来打开游标、获取游标记录和关闭游标。
sql
-- 声明游标
DECLARE 游标名 CURSOR FOR 查询语句;
-- 打开游标
OPEN 游标名;
-- 获取游标记录
FETCH 游标名 INTO 变量[, 变量] ...;
-- 关闭游标
CLOSE 游标名;

sql
-- 创建存储过程
CREATE PROCEDURE p11(IN class_id INT)
BEGIN
-- ⾸先声明变量
-- 定义学⽣姓名变量
DECLARE student_name VARCHAR(20);
-- 定义班级名变量
DECLARE class_name VARCHAR(20);
-- 定义游标
DECLARE s_cursor CURSOR FOR select s.`name` student_name, c.`name`
class_name from student s, class c where s.class_id = c.id and s.class_id =
class_id;
-- 创建新表
DROP TABLE IF EXISTS t_student_class;
CREATE TABLE IF NOT EXISTS t_student_class (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
student_name VARCHAR(20),
class_name VARCHAR(20)
);
-- 开启游标
OPEN s_cursor;
-- 遍历结果集
WHILE TRUE DO
-- 获取游标记录
FETCH s_cursor INTO student_name, class_name;
-- 写⼊新表
INSERT INTO t_student_class VALUES (NULL, student_name, class_name);
END WHILE;
-- 关闭游标
CLOSE s_cursor;
END;
-- 调⽤存储过程 出错
mysql> CALL p11(1);
ERROR 1329 (02000): No data - zero rows fetched, selected, or processed
由于while循环的退出条件是true,此时是⼀个死循环,当游标遍历完成之后继续向后遍历,发现没
有记录,所以报错,可以通过条件处理程序解决
条件处理程序

sql
DECLARE handler_action HANDLER
FOR condition_value [, condition_value] ...
statement
handler_action: {
CONTINUE -- 继续执⾏当前程序
| EXIT -- 终⽌执⾏当前程序
}
condition_value: {
mysql_error_code -- MYSQL错误码
| SQLSTATE [VALUE] sqlstate_value -- 状态码
| SQLWARNING -- 所有以01开头的SQLSTATE代码
| NOT FOUND -- 所有以02开头的SQLSTATE代码
| SQLEXCEPTION -- 所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE代码
}

sql
-- 创建存储过程
CREATE PROCEDURE p12(IN class_id INT)
BEGIN
-- ⾸先声明变量
-- 定义学⽣姓名变量
DECLARE student_name VARCHAR(20);
-- 定义班级名变量
DECLARE class_name VARCHAR(20);
-- 定义游标结束标识
DECLARE is_done bool DEFAULT FALSE;
-- 定义游标
DECLARE s_cursor CURSOR FOR select s.`name` student_name, c.`name`
class_name from student s, class c where s.class_id = c.id and s.class_id =
class_id;
-- 定义条件处理程序
DECLARE CONTINUE HANDLER FOR NOT FOUND SET is_done := TRUE;
-- 创建新表
DROP TABLE IF EXISTS t_student_class;
CREATE TABLE IF NOT EXISTS t_student_class (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
student_name VARCHAR(20),
class_name VARCHAR(20)
);
-- 开启游标
OPEN s_cursor;
-- 遍历结果集
read_loop: LOOP
-- 获取游标记录
FETCH s_cursor INTO student_name, class_name;
-- 退出循环
IF is_done THEN
LEAVE read_loop;
END IF;
-- 写⼊到新表
INSERT INTO t_student_class VALUES (NULL, student_name,class_name);
SELECT student_name;
END LOOP read_loop;
-- 关闭游标
CLOSE s_cursor;
END;
-- 调⽤存储过程
CALL p12(1);
存储函数
MySQL存储函数是有返回值的存储过程,参数只能是IN类型,类似于内置函数。存储函数与存储过程的主要区别在于存储函数必须有返回值,⽽存储过程则不⼀定。
sql
CREATE FUNCTION 存储函数名称 ([参数列表])
RETURNS type [characteristic ...]
BEGIN
-- SQL语句
RETURN ...;
END;
characteristic:
[NOT] DETERMINISTIC --表⽰相同的输⼊参数总是产⽣[不同]相同的结果
| NO SQL --不包含SQL语句
| READS SQL DATA --包含读取数据的语句,如select
| MODIFIES SQL DATA -- 包含写⼊数据的语句,如update,delete
-- 使⽤存储函数
select 存储函数名称 ([参数列表]);

sql
-- 定义存储函数
CREATE FUNCTION fun1(n INT)
RETURNS INT DETERMINISTIC -- 指定characteristic
BEGIN
-- 定义⼀个变量,表⽰结果
DECLARE total int DEFAULT 0;
-- 累加
WHILE n > 0 DO
SET total := total + n;
SET n := n - 1;
END WHILE;
-- 查询结果
RETURN total;
END;
-- 调⽤存储函数
select fun1(100);