目录
引言:
什么是存储过程?
一组为了完成特定功能的SQL语句集
经过编译之后,存储在数据库中,用户通过指定存储过程的名字和参数来执行,并获取相应的结果
存储过程
1.存储过程优缺点
优点:
1.性能优化:存储过程创建时,编译并保存在数据库中,执行过程比单个SQL语句快
2.代码复用:存储过程可以重复的调用,减少重复代码,提高代码的可维护性
3.安全性:限制用户直接访问数据库,而是通过存储过程间接访问数据库,从而提高其安全性
4.事务管理:可以在存储过程实现复杂的事务管理
5.降低耦合:当表结构发生改变时,只需要修改相应的存储过程,应用程序的修改较小
缺点:
1.可移植性差:存储过程不能跨数据库移植,更换数据库时需要重新编写
2.调试困难:只有少数数据库管理系统支持存储过程的调试,开发和维护困难
3.不适合高并发场景:在高并发场景下,存储过程可能会增加数据库的压力,难以维护
2.语法
-- 修改SQL语句结束标识符为//
DELIMITER //
-- 修改SQL语句结束标识符为;
DELIMITER ;
(1)创建:
-- 修改SQL语句结束标识符为//
DELIMITER //
-- 创建存储过程
CREATE PROCEDURE 存储过程名(参数列表)
BEGIN
-- SQL语句
END //
-- 修改SQL语句结束标识符为;
DELIMITER ;
(2)调用:
-- 调用存储过程
CALL 存储过程名(参数列表);
(3)查看:
-- 查看数据库中创建的存储过程
SELECT * FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = '数据库名';
-- 查看存储过程的定义
SHOW CREATE PROCEDURE 存储过程名;
(4)删除:
-- 删除存储过程
DROP PROCEDURE [IF EXISTS] 存储过程名;
3.变量
(1)系统变量(@@)
MySQL·服务器的配置变量,控制着服务器的行为和性能、
分为全局变量(GLOBAL)和会话变量(SESSION)
-- 查看所有系统变量
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;
设置系统变量:
SET [GLOBAL|SESSION] 系统变量名 = 值;
SET @@SESSION.系统变量名 = 值;
-- -------------------------------------------
-- ⽰例:设置事务⾃动提交会话变量为关闭/开启
SET @@SESSION.autocommit = 0;
SET autocommit = 1;
• 如果没有指定GLOBAL|SESSION,默认设置会话(SESSION)全局变量
• 会话关闭后,设置的会话(SESSION)变量失效;新建的会话读取全局系统变量的值做为初始值
• MySQL重启后,设置的全局(GLOBAL)变量失效,如果想使全局系统变量永久生效,需要修改选项文件
(2)用户定义变量(@)
在SQL会话中定义的变量
不用提前声明,作用域为当前会话
赋值:
-- ⽅式⼀
SET @var_name = expr [, @var_name]...;
-- ⽅式⼆ 【推荐】
SET @var_name := expr [, @var_name]...;
-- ⽅式三:在SELECT语句中 (赋值并查询)
SELECT @var_name := expr [, @var_name]...;
-- ⽅式四:查询结果赋值给⾃定义变量
SELECT 列名 INTO @var_name FROM 表名 WHERE ...;
(3)局部变量(DECLARE)
只在存储过程、函数、触发器中有效
需要使用declare声明,作用域范围在声明的BEGIN ... END中
声明:
DECLARE 变量名 变量类型 【DEFAULT 默认值】 ...;
赋值:
-- ⽅式⼀
SET var_name = 值;
-- ⽅式⼆ 【推荐】
SET var_name := 值;
-- ⽅式三:查询结果赋值给⾃定义变量
SELECT 列名 INTO var_name FROM 表名 WHERE ...;
• 变量名不区分大小写。
• 在存储过程和函数中,局部变量必须在使用前声明。
• 用户自定义变量在会话结束时失效,而局部变量在存储过程或函数结束时失效。
• 避免使用保留字作为变量名。
4.SQL编程
条件判断-IF
IF 条件1 THEN
......
[ELSEIF 条件2 THEN
......
ELSE
......]
END IF;
参数
分类:
|-------|-------------------------|
| 类型 | 描述 |
| IN | 输入类型,调用存储过程参数的值,默认参数类型 |
| OUT | 输出类型,可作为存储过程的返回值 |
| INOUT | 输入输出类型,既可以做输入类型,也可做输出类型 |
语法:
-- 修改SQL语句结束标识符为 //
DELIMITER //
-- 创建存储过程
CREATE PROCEDURE 存储过程名 ([IN/OUT/INOUT 参数名 参数类型] [,...])
BEGIN
-- SQL 语句
END //
-- 修改SQL语句结束标识符为 ;
DELIMITER ;
CASE
语法一:
-- 语法⼀
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list] ...
[ELSE statement_list]
END CASE
语法二:
-- 语法⼆
CASE
WHEN search_condition THEN statement_list
[WHEN search_condition THEN statement_list] ...
[ELSE statement_list]
END CASE
注意:
• 每个 statement_list 由⼀条或多条SQL语句组成且不允许为空。但可以使⽤BEGIN...END; 块。
• 如果没有 when_value 或 search_condition 与测试值匹配,并且 CASE 语句不包含 ELSE ⼦句,则会导致CASE语句错误。
循环
1.while
WHILE search_condition DO
statement_list
END WHILE;
• 先判断条件表达式 search_condition 是否为 TRUE ,如果条件成立,则执行循环体中的 statement_list
2.repeat
REPEAT
statement_list
UNTIL search_condition
END REPEAT;
• 先执⾏⼀次循环体中的 statement_list ,再先判断条件表达式 search_condition 是否为 TRUE ,如果条件成立,则继续执行循环体中的语句,如果条件不成立则退出循环
3.loop
[begin_label:] LOOP
statement_list
END LOOP [end_label]
LOOP也可以实现⼀个简单的循环,并且当满足某个条件时终止当前循环或退出整个循环,通常配合以下两个子句使用
LEAVE label :退出整个循环,类似于C++或JAVA中的 break;
ITERATE label :终止当前循环,进入下⼀次循环,类似于C++或JAVA中的 continue;
//• ⽰例:传⼊⼀个数n,累加从1累加到n之间偶数的值
-- 创建存储过程
CREATE PROCEDURE p10(IN n INT)
BEGIN
-- 定义⼀个变量,表⽰结果
DECLARE total int DEFAULT 0;
-- 累加
sum_label: LOOP
-- 判断是否退出
IF n <= 0 THEN
LEAVE sum_label;
END IF;
-- 判断是否偶数
IF n % 2 = 1 THEN
SET n := n - 1;
-- 跳出本次循环
ITERATE sum_label;
END IF;
-- 累加操作
SET total := total + n;
SET n := n - 1;
END LOOP sum_label;
-- 查询结果
SELECT total;
END;
-- 调⽤存储过程
CALL p10(100);
游标
MySQL中的游标是一种数据库对象,允许在存储过程和函数查询得到的结果集进行检索
MySQL中的游标是可读的,不能进行更新
-- 声明游标
DECLARE 游标名 CURSOR FOR 查询语句;
-- 打开游标
OPEN 游标名;
-- 获取游标记录
FETCH 游标名 INTO 变量[, 变量] ...;
-- 关闭游标
CLOSE 游标名;
• 使用游标之前必须先声明游标,之后使使用OPEN 、 FETCH 和 CLOSE 语句来打开游标、获取游标 记录和关闭游标。
• 游标必须 在条件处理程序之前被声明,并且变量必须在游标或条件处理程序之前被声明。
条件处理程序
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代码
}
• 定义条件是事先定义程序执⾏过程中可能遇到的问题
• 处理程序定义了在遇到问题时应当采取的处理方式
• 使用条件处理程序保证存储过程或函数在遇到警告或错误时能继续执行,可以增强程序处理问题的能力,避免程序异常停止运行。
存储函数
MySQL中的存储函数是有返回值的存储过程,参数只能是IN类型,类似于内置函数
存储函数存储过程:
最主要的区别就是存储函数一定有返回值,存储过程不一定有
• 在MySQL8.0版本中,如果binlog是开启的,那么在定义存储函数时,需要指定characteristic特 性,否则会报错
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 存储函数名称 ([参数列表]);
触发器
• 触发器是⼀个与表关联的数据库对象,在对表进行insert、update、delete操作时,触发并执行定义触发器时指定的SQL语句。
• 触发器可以在对表操作之前或之后执行,这被称为触发时间。
• 触发器可以执行SQL语句或逻辑块,用于实施复杂的业务逻辑或数据验证。
• MySQL⽀持三种类型的触发器:INSERT触发器、UPDATE触发器和DELETE触发器。使用OLD 和 NEW 关键字来引用触发器中发生变化的记录内容。
|-----------|------------------------------|
| 触发器类型 | OLD和NEW |
| INSERT触发器 | NEW表示将要或已经新增的数据 |
| UPDATE触发器 | OLD表示修改之前的数据,NEW表示将要或已经修改的数据 |
| DELETE触发器 | OLD表示将要或已经删除的数据 |
行级触发器和语句级触发器:
• 行级触发器:当对表中的每⼀⾏进⾏INSERT、UPDATE或DELETE操作时,行级触发器都会被触发。例如,如果执行⼀个UPDATE语句影响了多行数据,那么行级触发器会对每一行都触发一次。行级触发器可以访问受影响行的旧值和新值,常⽤于实现复杂的业务逻辑时对新旧值的访问。
• 语句级触发器:在整个INSERT、UPDATE或DELETE语句执行时只触发⼀次。无论该语句影响了多少行数据,语句级触发器都只在语句开始或结束时触发⼀次。语句级触发器主要用于实现⼀些全局性的操作,比如数据同步、数据清理等。
• MySQL只支持行级触器,不支持语句级触发器。
-- 创建
CREATE TRIGGER [IF NOT EXISTS] trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
BEGIN
trigger_stmt;
END;
trigger_time: { BEFORE | AFTER }
trigger_event: { INSERT | UPDATE | DELETE }
-- 查看
SHOW TRIGGERS;
-- 删除, 如果没有指定schema_name,默认为当前数据库
DROP TRIGGER [IF EXISTS] [schema_name.]trigger_name;