Mysql进阶---存储过程&变量&SQL编程

存储过程

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);
相关推荐
小陈工1 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
0xDevNull5 小时前
MySQL数据冷热分离详解
后端·mysql
科技小花5 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸5 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain5 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希6 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神6 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员6 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java6 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿7 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb