🌅主页:猫咪-9527-CSDN博客
"欲穷千里目,更上一层楼。会当凌绝顶,一览众山小。"
目录
[4. 存储过程](#4. 存储过程)
[4.1 IF 判断](#4.1 IF 判断)
[4.2 存储过程参数](#4.2 存储过程参数)
[4.2.1 IN 参数使用](#4.2.1 IN 参数使用)
[4.2.2 OUT 参数使用](#4.2.2 OUT 参数使用)
[4.2.3 INOUT 参数使用](#4.2.3 INOUT 参数使用)
[4.3 CASE 语句](#4.3 CASE 语句)
[4.4 WHILE 循环](#4.4 WHILE 循环)
[4.5 REPEAT 循环](#4.5 REPEAT 循环)
[4.6 LOOP 循环](#4.6 LOOP 循环)
[5.1 声明游标](#5.1 声明游标)
[5.2 打开游标](#5.2 打开游标)
[5.3 取出数据](#5.3 取出数据)
[5.4 关闭游标](#5.4 关闭游标)
[5.5 游标的完整示例](#5.5 游标的完整示例)
[6. 触发器](#6. 触发器)
[6.1 创建触发器](#6.1 创建触发器)
[6.2. 查看、删除触发器](#6.2. 查看、删除触发器)
[6.3. 注意事项与坑](#6.3. 注意事项与坑)
4. 存储过程
存储过程是由一组 SQL 语句构成的数据库对象,存储过程可以被存储在数据库中并在需要时执行。它支持控制流结构(如 IF
语句、CASE
语句、循环等),能够封装和重用数据库操作,提升代码的模块化和可维护性。
4.1 IF 判断
IF
语句是 MySQL 存储过程中的一种常见控制流结构,用于根据特定条件执行不同的 SQL 语句。它可以单独使用,也可以与 ELSE
和 ELSEIF
配合使用,从而形成多分支的条件判断结构。
语法:
IF condition THEN
-- 执行的 SQL 语句
ELSEIF condition THEN
-- 执行的 SQL 语句
ELSE
-- 执行的 SQL 语句
END IF;
示例:
DELIMITER $$
CREATE PROCEDURE CheckScore()
BEGIN
DECLARE score INT DEFAULT 58;
DECLARE result VARCHAR(10);
IF score >= 80 THEN
SET result := '优秀';
ELSEIF score >= 60 THEN
SET result := '及格';
ELSE
SET result := '不及格';
END IF;
SELECT result;
END$$
DELIMITER ;

4.2 存储过程参数
存储过程可以接受三种类型的参数:IN
、OUT
和 INOUT
。每种类型的参数有不同的作用,分别用于数据的输入、输出或同时输入输出。
语法:
CREATE PROCEDURE 存储过程名称 (IN/OUT/INOUT 参数名 参数类型)
BEGIN
-- SQL 语句
END;
4.2.1 IN 参数使用
IN
参数用于向存储过程传递输入值,存储过程内部可以使用这些值进行计算或查询操作。
示例:
DELIMITER $$
CREATE PROCEDURE CheckScoreIN(IN score INT)
BEGIN
DECLARE result VARCHAR(10);
IF score >= 80 THEN
SET result := '优秀';
ELSEIF score >= 60 THEN
SET result := '及格';
ELSE
SET result := '不及格';
END IF;
SELECT result;
END$$
DELIMITER ;

4.2.2 OUT 参数使用
OUT
参数用于存储过程返回值。执行存储过程时,返回结果将通过 OUT
参数传递给调用者。
示例:
DELIMITER $$
CREATE PROCEDURE CheckScoreOUT(IN score INT, OUT result VARCHAR(20))
BEGIN
IF score >= 80 THEN
SET result := '优秀';
ELSEIF score >= 60 THEN
SET result := '及格';
ELSE
SET result := '不及格';
END IF;
END$$
DELIMITER ;

4.2.3 INOUT 参数使用
INOUT
参数既可以作为输入值,也可以用作存储过程返回的输出值。
示例:
DELIMITER $$
CREATE PROCEDURE CheckScoreINOUT(INOUT score DOUBLE)
BEGIN
SET score := score * 0.5;
END$$
DELIMITER ;

4.3 CASE 语句
CASE
语句是一种条件表达式,允许根据不同的条件选择不同的结果。它通常用于 SELECT
查询、UPDATE
语句或存储过程中的条件判断。
语法:
CASE expression
WHEN value1 THEN result1
WHEN value2 THEN result2
WHEN value3 THEN result3
ELSE default_result
END
示例:
DELIMITER $$
CREATE PROCEDURE CheckGrade(IN score INT, OUT grade VARCHAR(3))
BEGIN
CASE
WHEN score BETWEEN 90 AND 100 THEN SET grade := 'A';
WHEN score BETWEEN 80 AND 89 THEN SET grade := 'B';
WHEN score BETWEEN 60 AND 79 THEN SET grade := 'C';
WHEN score BETWEEN 0 AND 59 THEN SET grade := 'D';
ELSE SET grade := 'NO';
END CASE;
END$$
DELIMITER ;
4.4 WHILE 循环
WHILE
循环是 MySQL 中的一种常见控制结构,用于在指定条件为 TRUE
时重复执行一组 SQL 语句,直到条件不再满足为止。
语法:
WHILE condition DO
-- 执行的 SQL 语句
END WHILE;
示例:
DELIMITER $$
CREATE PROCEDURE SumNumbers(IN n INT)
BEGIN
DECLARE a INT DEFAULT 0;
WHILE n >= 0 DO
SET a := a + n;
SET n := n - 1;
END WHILE;
SELECT a;
END$$
DELIMITER ;
4.5 REPEAT 循环
REPEAT
循环与 WHILE
循环类似,不同之处在于它在每次执行代码块后才检查条件,直到满足退出条件为止。
语法:
REPEAT
-- 执行的 SQL 语句
UNTIL condition
END REPEAT;
示例:
DELIMITER $$
CREATE PROCEDURE SumNumbersUsingRepeat(IN n INT)
BEGIN
DECLARE a INT DEFAULT 0;
REPEAT
SET a := a + n;
SET n := n - 1;
UNTIL n <= 0
END REPEAT;
SELECT a;
END$$
DELIMITER ;
4.6 LOOP 循环
LOOP
循环是另一种控制结构,适用于需要反复执行某些操作,直到满足特定退出条件时停止。与 REPEAT
和 WHILE
循环不同,LOOP
必须显式使用 LEAVE
语句来退出循环,否则它会无限执行。
语法:
loop_label : LOOP
-- 执行的 SQL 语句
IF condition THEN
LEAVE loop_label; -- 退出循环
END IF;
END LOOP;
示例:
DELIMITER $$
CREATE PROCEDURE SumOddNumbers(IN n INT)
BEGIN
DECLARE a INT DEFAULT 0;
sum_loop: LOOP
IF n % 2 = 0 THEN
SET n := n - 1;
ITERATE sum_loop;
END IF;
SET a := a + n;
SET n := n - 1;
IF n = 0 THEN
LEAVE sum_loop;
END IF;
END LOOP;
SELECT a;
END$$
DELIMITER ;
5.游标
在 MySQL 中,游标(Cursor)是一种用于遍历查询结果集的机制。游标允许你逐行处理查询结果,对于一些复杂的操作,比如逐行处理数据、进行复杂的计算等,游标非常有用。通常,游标用于存储过程中,在需要逐行处理结果集时使用。
5.1 声明游标
使用 DECLARE
语句声明游标。在声明游标时,需要指定游标基于的查询。
语法:
DECLARE 游标名 CURSOR FOR 查询语句;
5.2 打开游标
通过 OPEN
语句打开游标并执行查询。
语法:
OPEN 游标名;
5.3 取出数据
使用 FETCH
语句从游标中获取一行数据。
语法:
FETCH NEXT FROM 游标名 INTO 变量列表;
FETCH
会将游标当前指向的行的数据加载到指定的变量中。
5.4Handler (异常处理器)
在 MySQL 中,Handler(异常处理器) 是一种用于处理存储过程中的异常和错误的机制。Handler 可以帮助在遇到特定条件(如查询结果为空、错误发生等)时,自动执行指定的操作,从而提高程序的健壮性和灵活性。
MySQL 提供了多种 Handler 类型,主要通过 DECLARE
语句声明。在存储过程中,Handler 用于捕获特定条件(如查询没有返回结果、触发错误等),并执行相应的操作。
Handler 机制的目的是:当遇到异常或特定条件时,不需要让存储过程中断,而是可以继续执行后续的逻辑。
Handler 的基本语法:
DECLARE handler_type HANDLER FOR condition_value action;
-
handler_type
:指定处理器的类型,常见的类型有:-
CONTINUE
:遇到异常或条件时继续执行后续语句。 -
EXIT
:遇到异常或条件时退出存储过程或循环。
-
-
condition_value
:指定条件,可以是:-
NOT FOUND
:表示没有找到数据(通常与SELECT
查询结果为空时结合使用)02。 -
SQLWARNING
:表示 SQL 警告。01 -
SQLEXCEPTION
:表示 SQL 异常(如查询错误、插入失败等),除01和02
-
-
action
:在满足指定条件时要执行的操作,例如设置变量、打印错误信息、退出等。declare exit handler for sqlstate '02000';------退出码退出
declare exit handler for not found ;------------未找到退出01
declare exit handler for sqlwarning;------------sql警告退出02
5.4 关闭游标
当数据处理完毕后,使用 CLOSE
语句关闭游标。
语法:
CLOSE 游标名;
5.5 游标的完整示例
完整的示例:
delimiter ##
create procedure s10(in p_sdept varchar(10))
begin
declare c_snme varchar(20);
declare c_sno varchar(5);
declare c_birthday date;
declare c_name cursor for
select sno,snme,birthday from student where p_sdept=sdept;
declare exit handler for not found close c_name;
create table if not exists r1
( id varchar(5),
name varchar(20),
birthday date
);
open c_name;
while true do
fetch c_name into c_sno,c_snme,c_birthday;
insert into r1 values( c_sno,c_snme,c_birthday);
end while;
close c_name;
end ##
delimiter ;
6. 触发器
-
定义 :预先绑定在表上的一段 SQL 代码,当满足指定事件(
INSERT
/UPDATE
/DELETE
)并到达指定时机(BEFORE
/AFTER
)时,由 MySQL 自动执行。 -
作用:数据校验、自动补充派生字段、写审计日志、同步冗余表等。
-
执行粒度 :MySQL 只支持 行级触发器 (
FOR EACH ROW
),没有语句级触发器。对表里每一行受影响的记录都会执行一次触发器体。
6.1 创建触发器
创建触发器时,首先使用 DELIMITER
临时改变语句分隔符,以便在触发器体内使用分号。然后,通过 CREATE TRIGGER
语句创建触发器。
DELIMITER $$ -- 临时换分隔符,避免碰到触发器体内的分号
CREATE TRIGGER 触发器名
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON 表名
FOR EACH ROW
BEGIN
-- 触发器体:可写多条语句
-- 可用 NEW.列名 访问"新值"
-- 可用 OLD.列名 访问"旧值"(对于 INSERT 没旧值,DELETE 没新值)
END$$
DELIMITER ; -- 恢复默认分隔符
命名约定 :推荐
表名_事件_时机_trigger
,如student_ai_trigger
表示 A fter Insert 的触发器。
6.2. 查看、删除触发器
-- 查看当前库所有触发器
SHOW TRIGGERS\G
-- 查看某触发器的创建语句
SHOW CREATE TRIGGER student_ai_log\G
-- 删除触发器
DROP TRIGGER IF EXISTS student_ai_log;
6.3. 注意事项与坑
主题 | 说明 |
---|---|
权限 | 需要 TRIGGER 权限(或 SUPER )才能创建/删除。 |
单表同事件多触发器 | MySQL 8.0 允许同一 "事件+时机" 创建多个触发器,但 5.7 及更早版本只允许一个。 |
递归触发 | 触发器里对同一张表再 INSERT/UPDATE/DELETE 会再次触发,谨防无限递归。 |
事务 | 触发器在当前事务中执行,若触发器报错,整个外层语句会回滚。 |
NEW/OLD 只读限制 | BEFORE UPDATE 中可修改 NEW.xxx 来影响即将写入的值;其他场景 NEW /OLD 均只读。 |
不支持 COMMIT/ROLLBACK | 触发器体内禁止显式提交或回滚。 |
DELIMITER ##
CREATE TRIGGER tb_user_insert_trigger
AFTER INSERT ON student
FOR EACH ROW
BEGIN
INSERT INTO user_logs (operation, operate_time, operate_id, operate_params)
VALUES (
'insert',
NOW(),
NEW.sno,
CONCAT(
'插入的数据内容为:sno=', NEW.sno, ', name=', NEW.snme
)
);
END##
DELIMITER ;