MySQL进阶版第一课时

一、存储过程

1. 定义

一组为完成特定功能的 SQL 语句集,经编译后存储在数据库中,用户通过指定名称和参数执行并获取结果。

2. 特点

  • 封装性:业务逻辑封装在数据库内部,降低应用程序复杂度
  • 可维护性:集中管理数据库操作,便于更新维护
  • 可重用性:可多次调用,提升代码复用率

3. 优缺点

表格

优点 缺点
编译后存储,执行速度更快 跨数据库移植性差
代码可重用,减少重复编写 调试工具支持少,开发维护困难
限制直接表访问,提升安全性 高并发场景增加数据库压力
可实现复杂事务逻辑 -
表结构变更时,仅需修改存储过程,降低耦合 -

4. 核心语法

(1)创建

sql

复制代码
DELIMITER //  -- 修改结束标识符
CREATE PROCEDURE 存储过程名 (参数列表)
BEGIN
    -- SQL语句
END //
DELIMITER ;  -- 恢复默认结束标识符
(2)调用

sql

复制代码
CALL 存储过程名 (参数列表);
(3)查看

sql

复制代码
-- 查看指定数据库的所有存储过程
SELECT * FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = '数据库名';
-- 查看单个存储过程的定义
SHOW CREATE PROCEDURE 存储过程名;
(4)删除

sql

复制代码
DROP PROCEDURE [IF EXISTS] 存储过程名;

5. 简单示例

计算学生总分

sql

复制代码
CREATE PROCEDURE p_calAvg()
BEGIN
    select name, chinese + math + english as total from exam;
END;
CALL p_calAvg();

二、MySQL 变量

分为系统变量用户自定义变量局部变量三类,核心区别在于作用域和声明方式。

1. 系统变量

MySQL 服务器的配置变量,控制服务器行为和性能,分全局(GLOBAL)会话(SESSION)

(1)查看

sql

复制代码
-- 查看所有/指定系统变量
SHOW [GLOBAL|SESSION] VARIABLES;
SHOW [GLOBAL|SESSION] VARIABLES LIKE '变量名%';
-- 用SELECT查看
SELECT @@[GLOBAL|SESSION].系统变量名;
(2)设置

sql

复制代码
SET [GLOBAL|SESSION] 系统变量名 = 值;
SET @@SESSION.系统变量名 = 值;  -- 默认为SESSION

注意:会话变量随会话关闭失效,全局变量随 MySQL 重启失效,永久生效需修改配置文件。

2. 用户自定义变量

SQL 会话中定义,无需提前声明,作用域为当前会话。

(1)赋值(推荐:=,避免与等号判断冲突)

sql

复制代码
SET @var_name := 表达式;
SELECT 列名 INTO @var_name FROM 表名 WHERE 条件;
(2)使用

sql

复制代码
SELECT @var_name;

3. 局部变量

仅在存储过程、函数、触发器内有效,必须用 DECLARE 声明,作用域为 BEGIN...END 块内。

(1)声明

sql

复制代码
DECLARE 变量名 变量类型 [DEFAULT 默认值];
(2)赋值

sql

复制代码
SET var_name := 值;
SELECT 列名 INTO var_name FROM 表名 WHERE 条件;

4. 注意事项

  • 变量名不区分大小写
  • 局部变量必须先声明后使用,自定义变量无需声明
  • 自定义变量会话结束失效,局部变量随存储过程 / 函数结束失效
  • 避免使用 MySQL 保留字作为变量名

三、SQL 编程语法

1. 条件判断 - IF 语句

sql

复制代码
IF 条件1 THEN
    执行语句;
ELSEIF 条件2 THEN
    执行语句;
ELSE
    执行语句;
END IF;

2. 存储过程参数

分三类,用于实现参数的传入和返回,默认类型为 IN

表格

类型 描述
IN 输入参数,调用时传入值
OUT 输出参数,作为存储过程返回值
INOUT 输入输出参数,既传值又返回结果
语法

sql

复制代码
CREATE PROCEDURE 存储过程名 (IN 参1 类型, OUT 参2 类型, INOUT 参3 类型)
BEGIN
    -- SQL语句
END;

3. 分支选择 - CASE 语句

语法 1:值匹配

sql

复制代码
CASE 匹配值
    WHEN 目标值1 THEN 执行语句;
    WHEN 目标值2 THEN 执行语句;
    ELSE 执行语句;
END CASE;
语法 2:条件匹配

sql

复制代码
CASE
    WHEN 条件1 THEN 执行语句;
    WHEN 条件2 THEN 执行语句;
    ELSE 执行语句;
END CASE;

注意:无匹配项且无 ELSE 会报错,执行语句不能为空可加 BEGIN...END。

4. 循环语句

MySQL 支持 WHILE、REPEAT、LOOP 三种循环,LOOP 可配合 LEAVE(break)、ITERATE(continue)使用。

(1)WHILE(先判断后执行)

sql

复制代码
WHILE 条件 DO
    执行语句;
END WHILE;
(2)REPEAT(先执行后判断,至少执行 1 次)

sql

复制代码
REPEAT
    执行语句;
UNTIL 条件  -- 无分号
END REPEAT;
(3)LOOP(自定义循环,需手动退出)

sql

复制代码
标签名: LOOP
    IF 退出条件 THEN
        LEAVE 标签名;  -- 退出整个循环
    END IF;
    IF 跳过条件 THEN
        ITERATE 标签名;  -- 跳过本次循环
    END IF;
    执行语句;
END LOOP 标签名;

5. 游标

用于在存储过程 / 函数中逐行检索查询结果集 ,MySQL 中游标只读,不能更新。

核心语法(声明→打开→获取→关闭)

sql

复制代码
-- 声明(需在变量后、条件处理程序前)
DECLARE 游标名 CURSOR FOR 查询语句;
-- 打开
OPEN 游标名;
-- 逐行获取结果到变量
FETCH 游标名 INTO 变量1, 变量2,...;
-- 关闭
CLOSE 游标名;

问题 :游标遍历完后继续获取会报 1329 错误,需配合条件处理程序解决。

6. 条件处理程序

用于捕获程序执行中的错误 / 警告,定义处理方式,避免程序异常停止。

语法

sql

复制代码
DECLARE 处理方式 HANDLER FOR 异常类型 [,异常类型...] 执行语句;
-- 处理方式:CONTINUE(继续执行)、EXIT(终止执行)
-- 异常类型:MYSQL错误码/ SQLSTATE值/ SQLWARNING/ NOT FOUND/ SQLEXCEPTION

常用DECLARE CONTINUE HANDLER FOR NOT FOUND SET 标识变量 := TRUE;(捕获游标遍历完成的 NOT FOUND 异常)

7. 存储函数

返回值的存储过程,参数仅支持 IN 类型,类似 MySQL 内置函数。

语法(MySQL8.0 需指定 characteristic,binlog 开启时必加)

sql

复制代码
CREATE FUNCTION 函数名 (参数列表)
RETURNS 返回值类型 [DETERMINISTIC/NO SQL/READS SQL DATA/MODIFIES SQL DATA]
BEGIN
    -- SQL语句
    RETURN 返回值;
END;
-- 调用
SELECT 函数名 (参数列表);
characteristic 说明
  • DETERMINISTIC:相同输入返回相同结果
  • NO SQL:无 SQL 语句
  • READS SQL DATA:仅读取数据
  • MODIFIES SQL DATA:修改数据

8. 存储过程与存储函数的核心区别

表格

存储过程 存储函数
可有可无返回值 必须有返回值,且仅一个
支持 IN/OUT/INOUT 三种参数 仅支持 IN 类型参数
用 CALL 调用 用 SELECT 调用
可实现复杂业务逻辑、事务 适合简单的计算 / 查询,返回单一结果

四、触发器

1. 定义

与表关联的数据库对象,对表执行INSERT/UPDATE/DELETE 时自动触发执行指定 SQL,MySQL仅支持行级触发器(每操作一行触发一次),不支持语句级触发器。

2. 核心关键字

用于引用变更的记录数据,不同触发器类型可用关键字不同。

表格

触发器类型 可用关键字 关键字含义
INSERT NEW 新增 / 将要新增的数据
UPDATE OLD/NEW OLD = 修改前数据,NEW = 修改后数据
DELETE OLD 删除 / 将要删除的数据

3. 触发时间

  • BEFORE:操作表之前触发(用于数据验证、预处理)
  • AFTER:操作表之后触发(用于日志记录、数据同步)

4. 核心语法

(1)创建

sql

复制代码
DELIMITER //
CREATE TRIGGER [IF NOT EXISTS] 触发器名
触发时间 BEFORE/AFTER 触发事件 INSERT/UPDATE/DELETE
ON 表名 FOR EACH ROW  -- 行级触发器标识
BEGIN
    触发执行的SQL语句;
END //
DELIMITER ;
(2)查看

sql

复制代码
SHOW TRIGGERS;
(3)删除

sql

复制代码
DROP TRIGGER [IF EXISTS] [数据库名.]触发器名;

5. 典型场景

数据变更日志记录、数据一致性校验、关联表自动更新。


综合案例:学生信息管理系统(含存储过程 + 变量 + 游标 + 条件处理程序 + 存储函数 + 触发器)

案例需求

  1. 实现学生分数等级判定、1-n 累加计算的存储过程 / 函数
  2. 遍历指定班级学生,将信息同步到新表(游标 + 条件处理程序)
  3. 对学生表的增删改操作做日志记录(触发器)

前提准备:创建基础表

sql

复制代码
-- 班级表
CREATE TABLE IF NOT EXISTS class (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL COMMENT '班级名'
);
-- 学生表
CREATE TABLE IF NOT EXISTS student (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(20) NOT NULL,
    sno VARCHAR(20) UNIQUE NOT NULL,
    age INT,
    gender TINYINT COMMENT '1=男,2=女',
    enroll_date DATE,
    class_id INT,
    FOREIGN KEY (class_id) REFERENCES class(id)
);
-- 学生成绩表
CREATE TABLE IF NOT EXISTS exam (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    student_id BIGINT,
    chinese INT,
    math INT,
    english INT,
    FOREIGN KEY (student_id) REFERENCES student(id)
);
-- 插入测试数据
INSERT INTO class (name) VALUES ('Java一班'), ('Python二班'), ('大数据三班');
INSERT INTO student (name, sno, age, gender, enroll_date, class_id) 
VALUES ('唐三藏', '100001', 20, 1, '2024-09-01', 1),
       ('孙悟空', '100002', 19, 1, '2024-09-01', 1),
       ('曹操', '300001', 28, 1, '2024-09-01', 3);
INSERT INTO exam (student_id, chinese, math, english) 
VALUES (1, 88, 95, 90), (2, 92, 89, 85), (3, 78, 82, 80);

1. 存储过程:判定学生分数等级(IN+OUT 参数)

sql

复制代码
DELIMITER //
CREATE PROCEDURE p_get_score_level(IN score INT, OUT level VARCHAR(10))
BEGIN
    IF score >= 90 THEN
        SET level := '优秀';
    ELSEIF score >= 80 THEN
        SET level := '良好';
    ELSEIF score >= 60 THEN
        SET level := '及格';
    ELSE
        SET level := '不及格';
    END IF;
END //
DELIMITER ;
-- 调用
CALL p_get_score_level(88, @level);
SELECT @level;  -- 结果:良好

2. 存储函数:计算 1-n 的累加和(带 characteristic)

sql

复制代码
DELIMITER //
CREATE FUNCTION fun_sum_n(n INT)
RETURNS INT DETERMINISTIC
BEGIN
    DECLARE total INT DEFAULT 0;
    WHILE n > 0 DO
        SET total := total + n;
        SET n := n - 1;
    END WHILE;
    RETURN total;
END //
DELIMITER ;
-- 调用
SELECT fun_sum_n(100);  -- 结果:5050

3. 存储过程:游标 + 条件处理程序,同步指定班级学生到新表

sql

复制代码
DELIMITER //
CREATE PROCEDURE p_sync_student(IN c_id INT)
BEGIN
    -- 声明局部变量
    DECLARE s_name VARCHAR(20);
    DECLARE c_name VARCHAR(50);
    DECLARE is_done BOOLEAN DEFAULT FALSE;
    -- 声明游标
    DECLARE s_cursor CURSOR FOR 
        SELECT s.name, c.name FROM student s 
        JOIN class c ON s.class_id = c.id 
        WHERE s.class_id = c_id;
    -- 条件处理程序:捕获游标遍历完成
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET is_done := TRUE;
    -- 创建同步表
    DROP TABLE IF EXISTS t_student_sync;
    CREATE TABLE IF NOT EXISTS t_student_sync (
        id BIGINT PRIMARY KEY AUTO_INCREMENT,
        student_name VARCHAR(20),
        class_name VARCHAR(50)
    );
    -- 打开游标并遍历
    OPEN s_cursor;
    read_loop: LOOP
        FETCH s_cursor INTO s_name, c_name;
        IF is_done THEN
            LEAVE read_loop;
        END IF;
        -- 插入同步数据
        INSERT INTO t_student_sync (student_name, class_name) VALUES (s_name, c_name);
    END LOOP read_loop;
    -- 关闭游标
    CLOSE s_cursor;
    -- 查询同步结果
    SELECT * FROM t_student_sync;
END //
DELIMITER ;
-- 调用:同步Java一班(class_id=1)学生
CALL p_sync_student(1);

4. 触发器:学生表增删改操作日志记录

(1)创建日志表

sql

复制代码
CREATE TABLE IF NOT EXISTS student_log (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    operation_type VARCHAR(10) NOT NULL COMMENT 'insert/update/delete',
    operation_time DATETIME NOT NULL DEFAULT NOW(),
    operation_id BIGINT NOT NULL COMMENT '操作的学生ID',
    operation_data VARCHAR(500) COMMENT '操作数据'
);

(2)创建 INSERT 触发器

sql

复制代码
DELIMITER //
CREATE TRIGGER trg_student_insert
AFTER INSERT ON student FOR EACH ROW
BEGIN
    INSERT INTO student_log (operation_type, operation_id, operation_data)
    VALUES (
        'insert',
        NEW.id,
        CONCAT(NEW.id, ',', NEW.name, ',', NEW.sno, ',', NEW.age, ',', NEW.gender, ',', NEW.enroll_date, ',', NEW.class_id)
    );
END //
DELIMITER ;

(3)创建 UPDATE 触发器

sql

复制代码
DELIMITER //
CREATE TRIGGER trg_student_update
AFTER UPDATE ON student FOR EACH ROW
BEGIN
    INSERT INTO student_log (operation_type, operation_id, operation_data)
    VALUES (
        'update',
        NEW.id,
        CONCAT('旧数据:', OLD.id, ',', OLD.name, ',', OLD.sno, ',', OLD.age, ' | 新数据:', NEW.id, ',', NEW.name, ',', NEW.sno, ',', NEW.age)
    );
END //
DELIMITER ;

(4)创建 DELETE 触发器

sql

复制代码
DELIMITER //
CREATE TRIGGER trg_student_delete
AFTER DELETE ON student FOR EACH ROW
BEGIN
    INSERT INTO student_log (operation_type, operation_id, operation_data)
    VALUES (
        'delete',
        OLD.id,
        CONCAT(OLD.id, ',', OLD.name, ',', OLD.sno, ',', OLD.age, ',', OLD.gender)
    );
END //
DELIMITER ;

(5)测试触发器

sql

复制代码
-- 插入学生
INSERT INTO student (name, sno, age, gender, enroll_date, class_id) 
VALUES ('刘备', '300002', 27, 1, '2024-09-01', 3);
-- 更新学生
UPDATE student SET age = 26 WHERE name = '刘备';
-- 删除学生
DELETE FROM student WHERE name = '刘备';
-- 查看日志
SELECT * FROM student_log;

5. 存储过程:计算学生总分并判定等级(综合变量 + 查询)

sql

复制代码
DELIMITER //
CREATE PROCEDURE p_student_total_score(IN s_id BIGINT)
BEGIN
    DECLARE total INT DEFAULT 0;
    DECLARE level VARCHAR(10);
    -- 查询学生总分
    SELECT chinese + math + english INTO total FROM exam WHERE student_id = s_id;
    -- 调用分数等级存储过程
    CALL p_get_score_level(total, level);
    -- 查询结果
    SELECT s.name, total AS 总分, level AS 等级 
    FROM student s WHERE s.id = s_id;
END //
DELIMITER ;
-- 调用:查询唐三藏(id=1)的总分和等级
CALL p_student_total_score(1);
相关推荐
JuneXcy1 天前
第10章 数据库的安全与保护
数据库·mysql
liqianpin11 天前
完美解决phpstudy安装后mysql无法启动
数据库·mysql
cyber_两只龙宝1 天前
【MySQL】MySQL主从复制架构
linux·运维·数据库·mysql·云原生·架构
xiaoye37081 天前
docker 迁移mysql容器
mysql·docker
橘颂TA1 天前
【MySQL】内置函数
数据库·mysql
橘颂TA1 天前
【MySQL】使用C/C++来连接 MySQL
数据库·mysql
Y001112361 天前
Day2-MySQL-SQL-1
sql·mysql·oracle
艾莉丝努力练剑1 天前
【Linux进程间通信:共享内存】为什么共享内存的 key 值由用户设置
java·linux·运维·服务器·开发语言·数据库·mysql
天若有情6731 天前
【实战】从零开发企业级 B 端风格字符串值管理系统(Python+MySQL)
开发语言·python·mysql·企业级应用·b端应用
m0_635647481 天前
Qt开发与MySQL数据库教程(二)——MySQL常用命令以及示例
java·开发语言·数据库·mysql